2025 SEO best practices for Next.js including Core Web Vitals (INP replaces FID), E-E-A-T signals, Schema markup, AI content guidelines, and technical SEO. Use when optimizing pages for search engines, implementing metadata, adding structured data, or improving page speed.
Implements 2025 SEO best practices for Next.js including Core Web Vitals, E-E-A-T signals, and Schema markup. Use when optimizing metadata, adding structured data, or improving page speed for search engines.
/plugin marketplace add vanman2024/ai-dev-marketplace/plugin install nextjs-frontend@ai-dev-marketplaceThis skill is limited to using the following tools:
README.mdscripts/seo-audit.shPurpose: Implement 2025 SEO best practices for Next.js applications to maximize search visibility and organic traffic.
Activation Triggers:
Key Resources:
scripts/seo-audit.sh - Comprehensive SEO audit scriptscripts/validate-schema.sh - Validate JSON-LD structured datatemplates/metadata-patterns.tsx - Next.js Metadata API patternstemplates/schema-components.tsx - JSON-LD schema componentsexamples/complete-seo-setup.md - Full SEO implementation example| Metric | Target | What It Measures |
|---|---|---|
| LCP | < 2.5s | Largest Contentful Paint |
| INP | < 200ms | Interaction to Next Paint |
| CLS | < 0.1 | Cumulative Layout Shift |
// app/layout.tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
metadataBase: new URL('https://example.com'),
title: {
default: 'Site Name - Main Tagline',
template: '%s | Site Name',
},
description: 'Your site description for search engines (150-160 characters)',
keywords: ['keyword1', 'keyword2', 'keyword3'],
authors: [{ name: 'Author Name', url: 'https://author.com' }],
creator: 'Creator Name',
publisher: 'Publisher Name',
formatDetection: {
email: false,
address: false,
telephone: false,
},
}
export const metadata: Metadata = {
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://example.com',
siteName: 'Site Name',
title: 'Page Title for Social Sharing',
description: 'Description for social media (150-200 chars)',
images: [
{
url: '/og-image.png',
width: 1200,
height: 630,
alt: 'OG Image Alt Text',
},
],
},
}
export const metadata: Metadata = {
twitter: {
card: 'summary_large_image',
site: '@sitehandle',
creator: '@creatorhandle',
title: 'Title for Twitter',
description: 'Description for Twitter (150-200 chars)',
images: ['/twitter-image.png'],
},
}
export const metadata: Metadata = {
robots: {
index: true,
follow: true,
nocache: false,
googleBot: {
index: true,
follow: true,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
}
// app/blog/[slug]/page.tsx
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: { slug: string }
}
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const post = await getPost(params.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.coverImage,
width: 1200,
height: 630,
alt: post.title,
},
],
},
}
}
// app/sitemap.ts
import { MetadataRoute } from 'next'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://example.com'
// Static pages
const staticPages: MetadataRoute.Sitemap = [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 1,
},
{
url: `${baseUrl}/about`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: `${baseUrl}/pricing`,
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.9,
},
]
// Dynamic pages from database
const posts = await getAllPosts()
const postPages: MetadataRoute.Sitemap = posts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: 'weekly' as const,
priority: 0.6,
}))
return [...staticPages, ...postPages]
}
// app/robots.ts
import { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
const baseUrl = 'https://example.com'
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: ['/api/', '/admin/', '/_next/', '/private/'],
},
{
userAgent: 'GPTBot',
disallow: '/', // Block AI training crawlers if desired
},
],
sitemap: `${baseUrl}/sitemap.xml`,
}
}
// components/seo/OrganizationSchema.tsx
export function OrganizationSchema() {
const schema = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Company Name',
url: 'https://example.com',
logo: 'https://example.com/logo.png',
sameAs: [
'https://twitter.com/company',
'https://linkedin.com/company/company',
'https://github.com/company',
],
contactPoint: {
'@type': 'ContactPoint',
telephone: '+1-555-555-5555',
contactType: 'customer service',
availableLanguage: ['English'],
},
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
// components/seo/ArticleSchema.tsx
interface ArticleSchemaProps {
title: string
description: string
image: string
author: { name: string; url: string }
publishedAt: string
updatedAt: string
url: string
}
export function ArticleSchema(props: ArticleSchemaProps) {
const schema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: props.title,
description: props.description,
image: props.image,
author: {
'@type': 'Person',
name: props.author.name,
url: props.author.url,
},
publisher: {
'@type': 'Organization',
name: 'Company Name',
logo: {
'@type': 'ImageObject',
url: 'https://example.com/logo.png',
},
},
datePublished: props.publishedAt,
dateModified: props.updatedAt,
mainEntityOfPage: {
'@type': 'WebPage',
'@id': props.url,
},
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
// components/seo/FAQSchema.tsx
interface FAQItem {
question: string
answer: string
}
export function FAQSchema({ items }: { items: FAQItem[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: items.map((item) => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: {
'@type': 'Answer',
text: item.answer,
},
})),
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
// components/seo/BreadcrumbSchema.tsx
interface BreadcrumbItem {
name: string
url: string
}
export function BreadcrumbSchema({ items }: { items: BreadcrumbItem[] }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((item, index) => ({
'@type': 'ListItem',
position: index + 1,
name: item.name,
item: item.url,
})),
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}
// Prioritize above-fold images
import Image from 'next/image'
// Hero image with priority
<Image
src="/hero.jpg"
alt="Hero description"
width={1200}
height={600}
priority // Preloads image for LCP
sizes="(max-width: 768px) 100vw, 1200px"
/>
// app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
display: 'swap', // Prevents CLS
preload: true,
variable: '--font-inter',
})
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.variable}>
<body>{children}</body>
</html>
)
}
import Script from 'next/script'
// Defer non-critical scripts
<Script
src="https://analytics.example.com/script.js"
strategy="lazyOnload" // Load after page is interactive
/>
// Load immediately for critical scripts
<Script
src="https://critical.example.com/script.js"
strategy="afterInteractive"
/>
// components/AuthorBio.tsx
export function AuthorBio({ author }) {
return (
<div className="flex items-center gap-4 p-4 bg-muted rounded-lg">
<Image
src={author.avatar}
alt={author.name}
width={64}
height={64}
className="rounded-full"
/>
<div>
<h3 className="font-semibold">{author.name}</h3>
<p className="text-sm text-muted-foreground">{author.title}</p>
<p className="text-sm">{author.bio}</p>
<div className="flex gap-2 mt-2">
<a href={author.twitter}>Twitter</a>
<a href={author.linkedin}>LinkedIn</a>
</div>
</div>
</div>
)
}
# Run comprehensive SEO audit
./scripts/seo-audit.sh
# Checks:
# ✓ All pages have unique titles
# ✓ All pages have meta descriptions
# ✓ Open Graph tags present
# ✓ Twitter cards configured
# ✓ Sitemap.xml exists and valid
# ✓ Robots.txt configured
# ✓ Schema markup valid
# ✓ Images have alt text
# ✓ Heading hierarchy correct
# ✓ Internal linking structure
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.