Implementing JSON-LD in Next.js
Now that you understand structured data, let's implement JSON-LD in Next.js pages. We'll cover the basic pattern and create reusable components.
Basic Implementation
In Next.js, add JSON-LD as a script tag in your page component:
// app/blog/[slug]/page.tsx
export default async function BlogPost({ params }: Props) {
const { slug } = await params
const post = await getPost(slug)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: post.title,
description: post.excerpt,
image: post.coverImage,
datePublished: post.publishedAt,
dateModified: post.updatedAt,
author: {
'@type': 'Person',
name: post.author.name,
},
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<article>
<h1>{post.title}</h1>
{/* ... */}
</article>
</>
)
}
Creating a Reusable Component
For cleaner code, create a reusable JSON-LD component:
// components/JsonLd.tsx
type JsonLdProps = {
data: Record<string, unknown>
}
export function JsonLd({ data }: JsonLdProps) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
)
}
Usage:
import { JsonLd } from '@/components/JsonLd'
export default function Page() {
const articleData = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: 'My Article Title',
// ...
}
return (
<>
<JsonLd data={articleData} />
<article>{/* ... */}</article>
</>
)
}
Multiple Schema Types on One Page
You can include multiple JSON-LD blocks for different aspects of a page:
export default async function CoursePage({ params }) {
const course = await getCourse(params.slug)
const courseJsonLd = {
'@context': 'https://schema.org',
'@type': 'Course',
name: course.title,
description: course.description,
// ... course data
}
const breadcrumbJsonLd = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'Home', item: 'https://example.com' },
{ '@type': 'ListItem', position: 2, name: 'Courses', item: 'https://example.com/courses' },
{ '@type': 'ListItem', position: 3, name: course.title },
],
}
const organizationJsonLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'FreeAcademy',
url: 'https://freeacademy.ai',
// ... organization data
}
return (
<>
<JsonLd data={courseJsonLd} />
<JsonLd data={breadcrumbJsonLd} />
<JsonLd data={organizationJsonLd} />
<main>{/* ... */}</main>
</>
)
}
Type-Safe JSON-LD with TypeScript
Create type definitions for better safety:
// types/schema.ts
interface SchemaBase {
'@context': 'https://schema.org'
'@type': string
}
interface ArticleSchema extends SchemaBase {
'@type': 'Article' | 'BlogPosting' | 'NewsArticle'
headline: string
description?: string
image?: string | ImageObject
datePublished: string
dateModified?: string
author: PersonSchema | OrganizationSchema
}
interface PersonSchema extends SchemaBase {
'@type': 'Person'
name: string
url?: string
}
interface ImageObject {
'@type': 'ImageObject'
url: string
width?: number
height?: number
}
Usage:
const articleSchema: ArticleSchema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: post.title,
datePublished: post.publishedAt,
author: {
'@context': 'https://schema.org',
'@type': 'Person',
name: post.author.name,
},
}
Best Practices
1. Match Visible Content
Structured data must reflect the visible content on the page:
// Bad: Price in JSON-LD doesn't match the displayed price
const jsonLd = {
'@type': 'Product',
offers: { price: '99.99' }, // But page shows $149.99
}
// Good: JSON-LD matches visible content
const jsonLd = {
'@type': 'Product',
offers: { price: product.displayPrice },
}
2. Use Absolute URLs
Always use full URLs, not relative paths:
// Bad: Relative URLs
image: '/images/article.jpg'
// Good: Absolute URLs
image: 'https://example.com/images/article.jpg'
3. Use Proper Date Formats
Always use ISO 8601 format:
// Correct
datePublished: '2024-01-15T08:00:00+00:00'
dateModified: '2024-01-20T10:30:00Z'
// Incorrect
datePublished: 'January 15, 2024'
datePublished: '01/15/2024'
Summary
In this lesson, you learned:
- Basic JSON-LD implementation in Next.js
- Creating reusable JSON-LD components
- Adding multiple schema types to a page
- Type-safe structured data with TypeScript
- Best practices for implementation
In the next lesson, we'll explore common schema types for web applications.

