Add BlogPosting JSON-LD in a server component
Article markup helps Google understand headline, author, publish date and image, and feeds both rich results and AI answers. In a Next.js App Router project, render a JSON-LD script inside the blog page server component from the same fields the page displays. Keep the markup valid and identical to the visible content.
Why this matters
Serialize article markup on the server so crawlers see it in HTML.
Before you start
- A blog route that loads post data on the server
- Canonical post URL and absolute image URL available in the post record
How to apply it
Load the post in the server component
In app/blog/[slug]/page.tsx await params, load the post and keep the record in scope for both the JSX and the JSON-LD object, so markup and visible content share one source.
Check: curl -s your production domain/blog/<slug> contains the headline text without executing JavaScript.
Build the BlogPosting object
Set @context, @type BlogPosting, headline, description, datePublished, dateModified, author with name and url, image as absolute URLs, mainEntityOfPage and publisher.
Check: The Rich Results Test reports BlogPosting with no errors for headline, datePublished, author and image.
Render the JSON-LD script tag
Return a script element with type application/ld+json and dangerouslySetInnerHTML set to JSON.stringify(postSchema). Never interpolate raw CMS strings into the script body.
Check: curl -s your production domain/blog/<slug> | grep -c 'application/ld+json' returns 1 and the block parses with JSON.parse.
Match markup to visible content
Headline, author and dates in the JSON-LD must equal the rendered H1, byline and time element. Update or drop fields when the post changes so stale dates do not persist.
Check: For three posts the JSON headline and datePublished match the visible H1 and time datetime values.
Keep one representation
Remove microdata or duplicate JSON-LD for the same post, including blocks injected by SEO packages or template scripts, so one BlogPosting entity reaches the parser.
Check: The page source has one BlogPosting block and no itemprop articleBody duplication.
Source: seo-mcp playbook · curated entry · report an error
Official references
Framework notes
nextjs-app
Build the object in `app/blog/[slug]/page.tsx` and inject it with a `script type="application/ld+json"` tag whose inner HTML is `JSON.stringify(schema)`.
nextjs-pages
In the Pages Router add the script with `next/head` in `pages/blog/[slug].tsx`, or build it in `getStaticProps` from the same post object used for rendering.