Skip to content
Back to skills

Seo Optimizer

ASecurity

Technical SEO and GEO optimization for Next.js + shadcn/ui projects. Meta tags, Open Graph, JSON-LD structured data, sitemaps, robots.txt, Core Web Vitals, local/GEO SEO, hreflang, canonical URLs, and keyword optimization. Use when: user wants SEO, meta tags, structured data, sitemap, local SEO, or search optimization.

  • 6 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 27, 2026
developmenttypescriptgoreactnextjsnodegitapidatabaseperformance

Works with

  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add KevinZai/commander --skill seo-optimizer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Seo Optimizer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Seo Optimizer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kevinzai-seo-optimizer/badge)](https://www.skillsdirectory.com/skills/kevinzai-seo-optimizer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: seo-optimizer
description: "Technical SEO and GEO optimization for Next.js + shadcn/ui projects. Meta tags, Open Graph, JSON-LD structured data, sitemaps, robots.txt, Core Web Vitals, local/GEO SEO, hreflang, canonical URLs, and keyword optimization. Use when: user wants SEO, meta tags, structured data, sitemap, local SEO, or search optimization."
---

# SEO Optimizer Skill

Implement technical SEO for **Next.js (App Router) + shadcn/ui + Tailwind CSS + TypeScript** projects.

All output MUST be:
- Next.js App Router compatible (Metadata API, not `<Head>`)
- TypeScript strict mode
- Following Google's latest guidelines and Core Web Vitals

## 1. Next.js Metadata API

### Static Metadata
```tsx
// app/page.tsx or app/layout.tsx
import type { Metadata } from "next"

export const metadata: Metadata = {
  title: {
    default: "Site Name",
    template: "%s | Site Name", // for child pages
  },
  description: "Compelling 150-160 char description with primary keyword",
  keywords: ["keyword1", "keyword2"],
  authors: [{ name: "Author", url: "https://example.com" }],
  creator: "Brand Name",
  metadataBase: new URL("https://example.com"),
  alternates: {
    canonical: "/",
    languages: { "en-US": "/en", "es": "/es" },
  },
  openGraph: {
    type: "website",
    locale: "en_US",
    url: "https://example.com",
    siteName: "Site Name",
    title: "Page Title — can be longer for social",
    description: "Social description, can differ from meta description",
    images: [{ url: "/og-image.png", width: 1200, height: 630, alt: "Description" }],
  },
  twitter: {
    card: "summary_large_image",
    title: "Page Title",
    description: "Twitter description",
    images: ["/og-image.png"],
    creator: "@handle",
  },
  robots: {
    index: true,
    follow: true,
    googleBot: { index: true, follow: true, "max-video-preview": -1, "max-image-preview": "large", "max-snippet": -1 },
  },
  verification: {
    google: "google-verification-code",
  },
}
```

### Dynamic Metadata
```tsx
// app/blog/[slug]/page.tsx
import type { Metadata } from "next"

interface Props { params: Promise<{ slug: string }> }

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { slug } = await params
  const post = await getPost(slug)
  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: "article",
      publishedTime: post.publishedAt,
      modifiedTime: post.updatedAt,
      authors: [post.author.name],
      images: [{ url: post.ogImage, width: 1200, height: 630 }],
    },
    alternates: { canonical: `/blog/${slug}` },
  }
}
```

## 2. JSON-LD Structured Data

### Component Pattern
```tsx
export function JsonLd({ data }: { data: Record<string, unknown> }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
    />
  )
}
```

### Organization
```tsx
const orgSchema = {
  "@context": "https://schema.org",
  "@type": "Organization",
  name: "Company Name",
  url: "https://example.com",
  logo: "https://example.com/logo.png",
  sameAs: ["https://twitter.com/handle", "https://linkedin.com/company/name"],
  contactPoint: { "@type": "ContactPoint", telephone: "+1-XXX", contactType: "customer service" },
}
```

### Article / Blog Post
```tsx
const articleSchema = {
  "@context": "https://schema.org",
  "@type": "Article",
  headline: post.title,
  description: post.excerpt,
  image: post.ogImage,
  datePublished: post.publishedAt,
  dateModified: post.updatedAt,
  author: { "@type": "Person", name: post.author.name, url: post.author.url },
  publisher: { "@type": "Organization", name: "Site Name", logo: { "@type": "ImageObject", url: "/logo.png" } },
  mainEntityOfPage: { "@type": "WebPage", "@id": `https://example.com/blog/${post.slug}` },
}
```

### LocalBusiness (GEO SEO)
```tsx
const localSchema = {
  "@context": "https://schema.org",
  "@type": "LocalBusiness",
  name: "Business Name",
  image: "https://example.com/photo.jpg",
  "@id": "https://example.com",
  url: "https://example.com",
  telephone: "+1-XXX-XXX-XXXX",
  address: { "@type": "PostalAddress", streetAddress: "123 Main St", addressLocality: "City", addressRegion: "ST", postalCode: "12345", addressCountry: "US" },
  geo: { "@type": "GeoCoordinates", latitude: 40.7128, longitude: -74.0060 },
  openingHoursSpecification: [
    { "@type": "OpeningHoursSpecification", dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], opens: "09:00", closes: "17:00" },
  ],
  priceRange: "$$",
  aggregateRating: { "@type": "AggregateRating", ratingValue: "4.8", reviewCount: "127" },
}
```

### Product
```tsx
const productSchema = {
  "@context": "https://schema.org",
  "@type": "Product",
  name: "Product Name",
  image: ["https://example.com/photo1.jpg"],
  description: "Product description",
  brand: { "@type": "Brand", name: "Brand" },
  offers: {
    "@type": "Offer",
    price: "29.99",
    priceCurrency: "USD",
    availability: "https://schema.org/InStock",
    url: "https://example.com/product",
  },
}
```

### FAQ
```tsx
const faqSchema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  mainEntity: faqs.map(faq => ({
    "@type": "Question",
    name: faq.question,
    acceptedAnswer: { "@type": "Answer", text: faq.answer },
  })),
}
```

### BreadcrumbList
```tsx
const breadcrumbSchema = {
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  itemListElement: breadcrumbs.map((crumb, i) => ({
    "@type": "ListItem",
    position: i + 1,
    name: crumb.label,
    item: `https://example.com${crumb.href}`,
  })),
}
```

## 3. Sitemap & Robots

### sitemap.ts (App Router)
```tsx
// app/sitemap.ts
import type { MetadataRoute } from "next"

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts()
  const postUrls = posts.map(post => ({
    url: `https://example.com/blog/${post.slug}`,
    lastModified: new Date(post.updatedAt),
    changeFrequency: "weekly" as const,
    priority: 0.7,
  }))

  return [
    { url: "https://example.com", lastModified: new Date(), changeFrequency: "daily", priority: 1.0 },
    { url: "https://example.com/about", lastModified: new Date(), changeFrequency: "monthly", priority: 0.5 },
    ...postUrls,
  ]
}
```

### robots.ts
```tsx
// app/robots.ts
import type { MetadataRoute } from "next"

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      { userAgent: "*", allow: "/", disallow: ["/api/", "/admin/", "/_next/"] },
    ],
    sitemap: "https://example.com/sitemap.xml",
  }
}
```

## 4. Core Web Vitals

### LCP (Largest Contentful Paint)
- Use `priority` on hero images: `<Image priority src={...} />`
- Preload critical fonts: `<link rel="preload" as="font" />`
- Avoid lazy-loading above-the-fold content

### CLS (Cumulative Layout Shift)
- Always set `width` and `height` on `<Image>` (Next.js does this)
- Use `aspect-ratio` CSS for dynamic content
- Reserve space for ads/embeds with min-height

### INP (Interaction to Next Paint)
- Defer non-critical JS with `next/script` strategy="lazyOnload"
- Use `startTransition` for non-urgent state updates
- Avoid long tasks (>50ms) in event handlers

### Performance Component
```tsx
// Preload critical resources in layout.tsx
import Script from "next/script"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script src="https://analytics.example.com/script.js" strategy="lazyOnload" />
      </body>
    </html>
  )
}
```

## 5. GEO / Local SEO

### hreflang (Multi-language)
```tsx
// In metadata:
alternates: {
  canonical: "https://example.com",
  languages: {
    "en-US": "https://example.com/en",
    "es-ES": "https://example.com/es",
    "fr-FR": "https://example.com/fr",
    "x-default": "https://example.com",
  },
}
```

### Local Landing Pages
- Create `/locations/[city]` pages with unique content per location
- Include LocalBusiness schema with exact geo coordinates
- Embed Google Maps with proper `loading="lazy"`
- Add NAP (Name, Address, Phone) consistently across all pages

## 6. SEO Checklist

### Per-Page
- [ ] Unique `<title>` (50-60 chars) with primary keyword
- [ ] Unique `<meta description>` (150-160 chars) with CTA
- [ ] Canonical URL set
- [ ] Open Graph tags (title, description, image 1200x630)
- [ ] Twitter card tags
- [ ] H1 tag (one per page, contains primary keyword)
- [ ] Heading hierarchy (H1 > H2 > H3, no skips)
- [ ] Image alt text on all images
- [ ] Internal links to related content
- [ ] JSON-LD structured data appropriate for page type

### Site-Wide
- [ ] sitemap.xml generated and submitted
- [ ] robots.txt configured
- [ ] HTTPS everywhere
- [ ] Mobile responsive
- [ ] Core Web Vitals passing (LCP < 2.5s, CLS < 0.1, INP < 200ms)
- [ ] 404 page with navigation
- [ ] Redirects for changed URLs (301)
- [ ] No duplicate content / canonical tags set

**Version:** 1.0.0
**Last Updated:** 2026-03-17

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.