Enabling Semantic Linked Data in Your Next.js Blog
A Guide to JSON-LD Metadata
Metadata (the author, publication date, categories, and tags behind a post) is what lets search engines rank your content sensibly, generate rich snippets in results pages, and lets digital assistants and social platforms categorize what you write. While microdata embedded in HTML markup is one way to provide it, the most effective machine-readable format is , a standard for linked data that enables a .
JSON-LD is not woven into the page's visible HTML. It ships as a raw data block alongside it, which means crawlers can extract and interpret it without you touching the layout. In this post, I'll cover just enough of the concepts to be useful and then walk through how this blog implements it in Next.js.
Understanding JSON-LD
JavaScript Object Notation for Linked Data is a method of encoding linked data using JSON. JSON-LD 1.0 became a W3C Recommendation in January 2014, with JSON-LD 1.1 following in 2020. It uses the familiar JSON structure to express information using linked data vocabularies, most commonly , a collaborative project that provides shared vocabularies for marking up structured data on the internet.
In practice, a JSON-LD block is just a <script> tag of type application/ld+json in the page's markup. A minimal description of a blog post looks like this:
Search engines parse these blocks independently of your visible HTML, so you can describe your content as richly as you like without touching the layout. The rest of this post walks through how this very blog wires that up, and every file referenced lives in the if you want to lift the structure wholesale.
How This Blog Implements JSON-LD
The implementation is deliberately layered so that every fact lives in exactly one place:
- is the single source of identity truth: site title, description, canonical URL, and the author's social profiles. Every metadata surface (page metadata, JSON-LD, RSS, the manifest, and the sitemap) derives from this one object.
- holds the Schema.org entities as plain constants: the
Person, theWebSite, theBlog, and the organisations the author is affiliated with. - exports
linkedDataGenerator, which turns a post's frontmatter into a fullBlogPostingdescription at build time.
Describing the Author and the Site
The static entities are just exported objects. The author is a Schema.org Person that pulls its identity values from the shared metadata module instead of hard-coding them:
The WebSite entity reuses the same values, and the Blog entity simply derives from it by spreading and overriding the fields that differ:
Because these are plain constants that share one metadata source, a change like a new social profile or a domain move propagates to every JSON-LD block automatically.
Injecting the Site-Wide Entity
The root layout hands the WebSite entity to the layout container, which serializes it into the document head for every page:
Under the hood that renders the familiar script tag:
Generating a BlogPosting per Article
Each article page then adds its own BlogPosting block. The generator maps frontmatter onto Schema.org fields, including the tags as linked Thing entities, the reading-time word count, and the Open Graph image the post already generates for social cards:
The renders it next to the article content:
Since this runs in a server component at build time, the structured data ships as static HTML. There is no client-side JavaScript cost and crawlers see it on the first request.
Validating the Output
Before trusting any of this, verify what actually ships:
- View source: open any article, view the page source, and search for
ld+json. You should find two blocks, the site-wideWebSiteand the per-articleBlogPosting. - : paste an article URL and confirm the
BlogPostingis detected and eligible for rich results. - : a stricter, vendor-neutral check that surfaces typos in property names and malformed nesting that Google's tool quietly ignores.
Run these once after wiring things up and again whenever the generator changes; a single misspelled property fails silently otherwise.
Wrapping Up
Structured data is one of the cheapest SEO and interoperability wins available to a Next.js blog: a handful of constant objects, one generator function, and two script tags. The layering matters more than the volume, so keep identity facts in one module, derive the Schema.org entities from it, and generate per-article descriptions from the frontmatter you already maintain.
The complete, working setup, from the metadata module to the generator and the injection points, is in the . Lift it, adapt the vocabulary to your content types, and let the machines finally understand what you write.