Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add soilmass/vibe-coding-plugin --skill "nextjs-metadata"
Install specific skill from multi-skill repository
# Description
>
# SKILL.md
name: nextjs-metadata
description: >
Next.js 15 Metadata API β static/dynamic metadata, generateMetadata, generateViewport, OpenGraph, Twitter cards, sitemap.ts, robots.ts, JSON-LD
allowed-tools: Read, Grep, Glob
Next.js Metadata
Purpose
Next.js 15 Metadata API for SEO and social sharing. Covers static and dynamic metadata,
OpenGraph, structured data, and sitemaps. The ONE skill for head tags and SEO.
When to Use
- Setting page titles, descriptions, and OpenGraph tags
- Generating dynamic metadata from database content
- Creating sitemaps and robots.txt
- Adding structured data (JSON-LD)
When NOT to Use
- CSP security headers β
security - Middleware headers β
nextjs-middleware - Route-level data fetching β
nextjs-data
Pattern
Static metadata
// app/layout.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
template: "%s | My App",
default: "My App",
},
description: "A Next.js 15 application",
metadataBase: new URL("https://myapp.com"),
};
Dynamic metadata with generateMetadata
// app/blog/[slug]/page.tsx
import type { Metadata } from "next";
export async function generateMetadata({
params,
}: {
params: Promise<{ slug: string }>;
}): 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,
images: [post.coverImage],
},
};
}
generateViewport (separate from metadata)
import type { Viewport } from "next";
export const viewport: Viewport = {
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#ffffff" },
{ media: "(prefers-color-scheme: dark)", color: "#000000" },
],
width: "device-width",
initialScale: 1,
};
sitemap.ts
// app/sitemap.ts β see `seo-advanced` for full sitemap patterns
import type { MetadataRoute } from "next";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await db.post.findMany({ select: { slug: true, updatedAt: true } });
return posts.map((post) => ({
url: `https://myapp.com/blog/${post.slug}`,
lastModified: post.updatedAt,
}));
}
Anti-pattern
// WRONG: putting viewport config in metadata export
export const metadata: Metadata = {
themeColor: "#000", // Moved to viewport in Next.js 15
viewport: "width=device-width", // Also moved to viewport
};
In Next.js 15, viewport-related fields are in a separate viewport export.
Common Mistakes
- Putting viewport config in metadata β use separate
viewportexport - Not awaiting params in
generateMetadataβ they're Promises in Next.js 15 - Missing
metadataBaseβ relative OpenGraph image URLs break - Forgetting title template in root layout β inconsistent page titles
- Not setting
robots.txtto block private routes
Checklist
- [ ] Root layout has
metadatawith title template and description - [ ]
viewportexported separately frommetadata - [ ] Dynamic pages use
generateMetadatawith awaited params - [ ]
metadataBaseset for proper OpenGraph URL resolution - [ ]
sitemap.tsandrobots.tsexist for SEO
Composes With
nextjs-routingβ metadata is tied to route segmentsnextjs-dataβ generateMetadata fetches data for dynamic pagestailwind-v4β theme colors reference CSS custom propertiesi18nβ locale-aware metadata with alternates and hreflang
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.