install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/next-metadata-api" ~/.claude/skills/intense-visions-harness-engineering-next-metadata-api-c62ddc && rm -rf "$T"
manifest:
agents/skills/codex/next-metadata-api/SKILL.mdsource content
Next.js Metadata API
Define SEO metadata, Open Graph tags, and dynamic OG images with the Metadata API
When to Use
- Setting page titles, descriptions, and canonical URLs
- Generating Open Graph and Twitter Card metadata for social sharing
- Creating dynamic OG images for blog posts, product pages, or any dynamic content
- Configuring robots.txt behavior, sitemaps, and web app manifests
- Overriding metadata from a parent layout in a child page
Instructions
- Export a
constant from anymetadata
orlayout.tsx
for static metadata.page.tsx - Export an async
function for dynamic metadata derived from route parameters or fetched data.generateMetadata({ params, searchParams }) - Use the
property in roottitle.template
to set a consistent title suffix — child pages set only the page-specific prefix.layout.tsx - Pass
in root metadata to resolve relative URLs inmetadataBase
andopenGraph.images
.twitter.images - Create
in any route segment to generate a dynamic OG image withopengraph-image.tsx
— no external service needed.@vercel/og - Use
,robots
, andcanonical
fields to control crawler behavior and avoid duplicate content.alternates - Metadata is merged from parent layouts down to child pages — child values override parent values for the same key.
- Never duplicate metadata in both
andlayout.tsx
for the same route — define at the most specific level.page.tsx
// app/layout.tsx — root metadata with template and base URL export const metadata: Metadata = { metadataBase: new URL('https://example.com'), title: { default: 'My App', template: '%s | My App' }, description: 'The best app in the world', openGraph: { type: 'website', locale: 'en_US', siteName: 'My App' }, }; // app/posts/[slug]/page.tsx — dynamic metadata import type { Metadata } from 'next'; export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> { const post = await fetchPost(params.slug); return { title: post.title, // becomes "Post Title | My App" via template description: post.excerpt, openGraph: { title: post.title, description: post.excerpt, images: [{ url: `/api/og?title=${encodeURIComponent(post.title)}`, width: 1200, height: 630 }], }, }; } // app/posts/[slug]/opengraph-image.tsx — dynamic OG image import { ImageResponse } from 'next/og'; export const runtime = 'edge'; export const size = { width: 1200, height: 630 }; export const contentType = 'image/png'; export default async function Image({ params }: { params: { slug: string } }) { const post = await fetchPost(params.slug); return new ImageResponse(<div style={{ fontSize: 48 }}>{post.title}</div>); }
Details
The Metadata API replaces
<Head> from next/head in the App Router. It is declarative, type-safe, and tree-mergeable — parent layout metadata flows down to child pages, with child values winning on conflict.
: The root layout sets title.template
title: { default: 'My App', template: '%s | My App' }. Every child page that sets title: 'Dashboard' automatically becomes 'Dashboard | My App'. Pages that do not set a title fall back to default.
: Required when any metadata field uses a relative URL (especially metadataBase
openGraph.images). Without it, Next.js warns and relative URLs are not resolved correctly for social crawlers.
Dynamic OG images with
: Place this file in any route segment directory. It exports an opengraph-image.tsx
ImageResponse and runs on the Edge Runtime. Next.js automatically serves it at /{route}/opengraph-image and links it from the page's <meta> tags. No additional configuration needed.
Deduplication: Next.js merges metadata from all parent layouts. If a parent sets
openGraph.images and a child does not, the parent's images are used. If the child sets openGraph.images, it replaces the parent's entirely — not merged.
Sitemap and robots: Create
app/sitemap.ts (returns MetadataRoute.Sitemap) and app/robots.ts (returns MetadataRoute.Robots) for programmatic control of these files.
Source
https://nextjs.org/docs/app/building-your-application/optimizing/metadata
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.