Subscribe to 💌 Tiny Improvements, my weekly newsletter for product builders. It's a single, tiny idea to help you build better products.

Add Structured Data to your Next.js site with JSON-LD for better SEO

Structured Data can be added to your site tell Google and other search engines what type of content is on each page using a metadata format called JSON-LD.

note: This post is inspired by an article that my pal Josh Finnie wrote, showing how he added JSON-LD to his Astro.js site: If you're an Astro-naut - give that a read. It's great!

SEO is the growth hack you're overlooking

Many devs overlook the value of SEO for their work.

I've long contended that learning a little bit about SEO can go a long way. It's in your best interest to configure your site so that Google, Duck Duck Go, and Bing know what your site is about, and what you're expert on.

In this brief tutorial, I'll share some information about Structured Data and JSON-LD, and how you can add it to your Next.js site to improve your site's SERP (Search Engine Results Page) presentation, with just a little bit of code and configuration.

What is JSON-LD?

JSON-LD (JSON Lightweight Linked Data format) is a metadata format which can be added to your site to tell Google and other search engines what type of content is on each page. This can help search engines understand your content better, and can lead to better search results for your site. In reality, JSON-LD is just a specific format for JSON which is human readable, and is meant to describe the content on a page in a way that search engines can understand. You can read more about it here: https://json-ld.org/

JSON-LD looks like this:

1
{
2
"@context": "https://json-ld.org/contexts/person.jsonld",
3
"@id": "http://dbpedia.org/resource/John_Lennon",
4
"name": "John Lennon",
5
"born": "1940-10-09",
6
"spouse": "http://dbpedia.org/resource/Cynthia_Lennon"
7
}

What is Structured Data?

Structured Data is a set of search-engine specific JSON-LD templates that search crawlers look for to understand more about the information you're sharing online. It is optional for your site, but can help bring more traffic to your site by increasing the chances that your site will show up in search results.

Adding the right type of Structured data to your site means that when your pages come up in someone's search results, the search engine can display more information about your site, like reviews, ratings, and other rich snippets. Ever seen a recipe or a product review in a search result? That's because of structured data.

A search result featuring apple cobbler recipes, with a star rating, time to complete, a short ingredients list, and a photo
Structured Data can help your site show up in search results with rich snippets like this one

Types of Structured Data

There are many types of Structured Data you can embed on your site - Google Search Central has a list of structured data features which that is worth a good look. Some examples of Structured Data include:

  • Article: For news, blog, and other article pages
  • Events: Showcases events like concerts, festivals, and other happenings
  • FAQ: Make your FAQ page more discoverable
  • Product: Details and information about a product you're selling
  • Recipe: Ingredients, cook time, and other details about a recipe
  • Review Snippet: A review of a product, service, or other item
  • Video: For pages that showcase video content

Each of these formats are slightly different in content, but they use JSON-LD to describe the content in a way that search engines can understand. By way of example, this is the Structured Data for a post on my site about a livestream I did demonstrating how to build a site with Astro, TypeScript, and React:

1
<script type="application/ld+json">
2
{
3
"@context": "https://schema.org",
4
"@type": "VideoObject",
5
"name": "Rebuilding an open source content-rich site with Astro, TypeScript, and React",
6
"description": "A YouTube live coding stream, learning to build content-driven sites with the Astro Web Framework.",
7
"thumbnailUrl": "https://i.ytimg.com/vi/wyJYInvZya8/hqdefault.jpg",
8
"uploadDate": "2024-03-23T00:00:00.000Z",
9
"contentUrl": "https://www.youtube.com/watch?v=wyJYInvZya8",
10
"embedUrl": "https://www.youtube.com/embed/wyJYInvZya8",
11
"duration": "PT1M33S"
12
}
13
</script>

Not too bad, right? This JSON-LD tells Google that this page is a video, and gives Google some information about the video, like the title, description, and thumbnail. In theory, if someone searches for the title of the video, Google might show this video in the search results, with a thumbnail and a link to the video.

Once you've got Structured Data embedded on your site, you can verify that it's working using Schema.org's Structured Data Testing Tool:

Schema.org's Structured Data Testing Tool showing that my video structured data is valid

Similarly, you can use Google's Rich Results Test:

Google's Rich Results Test showing that my video structured data is valid

If you want to poke around yourself, check out the results for the video example above.

Pretty cool! So - now let's talk about how it's done.

Adding Structured data to your Next.js site with JSON-LD

My site is built with Next.js - the articles you read here are written in an extension of Markdown called MDX, and the site is statically generated.

When I publish an article that features a YouTube video, I add frontmatter to the MDX file for the article that contains the ID of the video, and then I use that ID to generate the JSON-LD for the video.

Again, for the page above, that looks like this:

1
---
2
title: 'Rebuilding an open source content-rich site with Astro, TypeScript, and React'
3
excerpt: A YouTube live coding stream, learning to build content-driven sites with the Astro Web Framework.
4
date: 2024-03-23
5
tags: [video, nextjs, typescript, astro]
6
coverImagePublicId: posts/live-astro-content-driven-website-rebuild/cover
7
path: live-astro-content-driven-website-rebuild
8
youTubeId: wyJYInvZya8
9
---
10
11
OpenApi.tools is an Open Source yada yada yada

Google provides an npm package called schema-dts which contains types and helpers for generating JSON-LD. Start by installing this package:

1
npm install schema-dts
2
# or with pnpm
3
pnpm install schema-dts
4
# or with yarn
5
yarn add schema-dts
6
# or with bun
7
bun add schema-dts

Then, import the package and use it to generate a typed variable to contain a VideoObject's schema:

1
import type { VideoObject, WithContext } from 'schema-dts';
2
3
let videoStructuredData: WithContext<VideoObject> | undefined = undefined;

Shout out to redditor /u/AdrnF for the tip. I didn't know this npm package existed!

Next, we'll need to populate this variable with the appropriate data for the schema. In my case, I'm using the youTubeId from the frontmatter to generate the JSON-LD for the video, like this:

1
let videoStructuredData: VideoStructuredData | undefined = undefined;
2
3
if (youTubeId) {
4
videoStructuredData = {
5
'@context': 'https://schema.org',
6
'@type': 'VideoObject',
7
name: title,
8
description: excerpt,
9
thumbnailUrl: `https://i.ytimg.com/vi/${youTubeId}/hqdefault.jpg`, // this is the thumbnail for the video straight from youtube
10
uploadDate: new Date(date).toISOString(),
11
contentUrl: `https://www.youtube.com/watch?v=${youTubeId}`, // this is the URL for the video on youtube
12
embedUrl: `https://www.youtube.com/embed/${youTubeId}`, // this is the URL for the video embed on youtube
13
duration: 'PT1M33S', // this is the duration of the video
14
15
/*
16
note that I'm not using the entire VideoObject schema here, but
17
you could add more fields if you wanted to, like interactionStatistic
18
and regionsAllowed:
19
*/
20
21
// interactionStatistic: {
22
// '@type': 'InteractionCounter',
23
// interactionType: { '@type': 'http://schema.org/WatchAction' },
24
// userInteractionCount: 0,
25
// },
26
// regionsAllowed: ['US'],
27
};
28
}

Once this object is created, I serialize it to a string and add it to a <script> tag on the page - depending on when and where you generate it, you can stick this in the <head> of your page, or right in the body of the document, like I do:

1
2
// snip -
3
4
return (
5
<>
6
{videoStructuredData && (
7
<script
8
type="application/ld+json"
9
dangerouslySetInnerHTML={{
10
__html: JSON.stringify(videoStructuredData),
11
}}
12
/>
13
)}
14
<article>
15
{/* page content goes here */}
16
</article>
17
</>
18
);
19

When the page is generated, you can inspect the source and see the JSON-LD embedded in the page - as in the example above.

You can double-check that your Structured data is valid using one the Schema.org Structured Data Testing Tool before publishing by copy/pasting the generated HTML for your pages into the "code snippet" tab. Once you're happy with it, you can publish your article and you're good to go!

Job done. Google's crawler will see this JSON-LD and use it to understand the content on your page better, and you'll be on your way to better search results.

Embed your Structured data server-side for best results

Embedding this information on in your articles in Next.js only takes a few steps, but it's critical that you do this server-side to give google the best chances of seeing it. I'm using the Next.js /pages directory for my site -- articles are generated statically using getStaticPaths and getStaticProps, which means that the JSON-LD is generated at build time.

If you use the App Router, you'll want to make sure that you embed your JSON-LD using a React Server Component (RSC), not using a client-side script.

If you're using a CMS, you'll want to make sure that the JSON-LD is generated when the page is statically rendered, and not when the page is loaded in the browser.

Challenges with structured data

It's worth mentioning here that even if the schema validators can read your JSON-LD, it doesn't guarantee that Google will use it in search results. Google's search algorithms are complex, and they use many signals to determine what to show in search results.

I've been tweaking the implementation of Video structured data on posts like the one above, because the Google Search Console has been showing me that the structured data is valid, but it's not showing up in search results as a video:

Google Search Console showing that my video structured data is valid, but not showing up in search results
Google's crawler thinks my video isn't the main content of the page.

There's still some work to do here, but I'm confident that with some more tweaking, I'll be able to get this working. If you know more about this, I'd love to hear from you! Drop me a line on Threads @irreverentmike or email me hello@mikebifulco.com.

These rules are always changing, and keeping up with them is a fulltime job in itself. Nonetheless, adding structured data to your site is a good practice, and can help your site show up in more search results -- even if you can't control exactly how it shows up.

Keep adding Structured Data to your site

Now's your turn - go give it a shot. You may want to start with something like the Profile Page structured data format, or by adding one for Articles to your posts.

For the amount of effort required, the payoff is worth it. Every little SEO boost you build into your site pays off in the long run. Think of it as an investment in the passive value of your content - the more you can do to make your content discoverable, the more likely it is to be found.

Resources on SEO and Structured Data

As I mentioned above, this tutorial was inspired by Josh Finnie's article on adding JSON-LD to his Astro.js site. Josh is a great developer and a good friend, and I recommend you check out his work.

I've written a few articles on SEO that you may find helpful:

And here are some tools that can help you get started with Structured Data:

If you found this helpful I'd love it if you shared the article with a friend.

I'm also keen to hear from you. Drop me a line on Threads @irreverentmike or email me hello@mikebifulco.com if you think I missed anything, or if you have any questions!

Hero
Add Structured Data to your Next.js site with JSON-LD for better SEO

Structured Data can be added to your site tell Google and other search engines what type of content is on each page using a metadata format called JSON-LD.

seonextjstypescript
***

SHIP PRODUCTS
THAT MATTER

💌 Tiny Improvements: my weekly newsletter sharing one small yet impactful idea for product builders, startup founders, and indiehackers.

It's your cheat code for building products your customers will love. Learn from the CTO of a Y Combinator-backed startup, with past experience at Google, Stripe, and Microsoft.

    Join the other product builders, and start shipping today!