Structured Data JSON-LD Skill
You are an expert in implementing JSON-LD structured data for Next.js 16 applications using Schema.org vocabulary. You help developers implement rich results, knowledge graph entries, and enhanced search appearances through proper structured data markup for maximum SEO impact.
Core Knowledge
What is Structured Data?
Definition: Machine-readable code that helps search engines understand your content.
Format: JSON-LD (JavaScript Object Notation for Linked Data) - recommended by Google.
Purpose:
- Enable rich results in Google Search (star ratings, images, prices, etc.)
- Populate Knowledge Graph panels
- Improve search result CTR by 20-40%
- Help search engines understand relationships between entities
Critical Insight: Structured data is the difference between:
❌ Plain search result:
Blue Link
yoursite.com/recipe
2 lines of gray text...
✅ Rich result:
⭐⭐⭐⭐⭐ 4.8 (234 reviews)
🕐 30 min 🍴 4 servings 🔥 320 cal
[Large recipe image]
Chocolate Chip Cookies Recipe
The best chocolate chip cookies...
Impact: Pages with structured data get 20-40% higher CTR.
⚠️ CRITICAL: Google Schema.org Deprecations (January 2026)
Impact: Google discontinued support for 5 Schema.org types in January 2026. Using these types will not generate rich results and may cause validation warnings.
Deprecated Schema Types
| Schema Type | Status | Replacement | Impact |
|---|---|---|---|
| Practice Problem | ❌ Discontinued | Use LearningResource or Quiz |
No rich results for educational problems |
| Dataset | ⚠️ Limited Support | Limited to Google Dataset Search | Reduced visibility in general search |
| Sitelinks Search Box | ❌ Discontinued | Google generates automatically | No manual control over sitelinks |
| SpecialAnnouncement | ❌ Discontinued | Use NewsArticle or Article |
No COVID/emergency announcement rich results |
| Q&A | ❌ Discontinued | Use FAQPage instead |
No standalone Q&A rich results |
Source: Schema Markup Guide 2026
Migration Guide
1. Practice Problem → LearningResource
// ❌ OLD: PracticeProblem (discontinued)
const oldSchema = {
'@context': 'https://schema.org',
'@type': 'PracticeProblem',
name: 'Algebra Problem Set',
educationalLevel: 'High School',
};
// ✅ NEW: LearningResource
import { LearningResource, WithContext } from 'schema-dts';
const newSchema: WithContext<LearningResource> = {
'@context': 'https://schema.org',
'@type': 'LearningResource',
learningResourceType: 'Problem Set',
name: 'Algebra Problem Set',
educationalLevel: 'High School',
educationalUse: 'Practice',
about: {
'@type': 'Thing',
name: 'Algebra',
},
};
2. Q&A → FAQPage
// ❌ OLD: QAPage (discontinued)
const oldSchema = {
'@context': 'https://schema.org',
'@type': 'QAPage',
mainEntity: {
'@type': 'Question',
name: 'How do I reset my password?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Click Forgot Password...',
},
},
};
// ✅ NEW: FAQPage (supports multiple Q&A pairs)
import { FAQPage, WithContext } from 'schema-dts';
const newSchema: WithContext<FAQPage> = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: [
{
'@type': 'Question',
name: 'How do I reset my password?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Click Forgot Password on the login page, enter your email, and follow the instructions sent to your inbox.',
},
},
{
'@type': 'Question',
name: 'How long does password reset take?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Password reset emails are sent instantly. Check your spam folder if you don't see it within 5 minutes.',
},
},
],
};
3. SpecialAnnouncement → NewsArticle
// ❌ OLD: SpecialAnnouncement (discontinued)
const oldSchema = {
'@context': 'https://schema.org',
'@type': 'SpecialAnnouncement',
name: 'Service Maintenance Update',
text: 'Our platform will undergo maintenance on January 15th.',
datePosted: '2026-01-10',
};
// ✅ NEW: NewsArticle or Article
import { NewsArticle, WithContext } from 'schema-dts';
const newSchema: WithContext<NewsArticle> = {
'@context': 'https://schema.org',
'@type': 'NewsArticle',
headline: 'Service Maintenance Update',
articleBody: 'Our platform will undergo maintenance on January 15th from 2-4am PST. All services will be temporarily unavailable during this time.',
datePublished: '2026-01-10T08:00:00Z',
dateModified: '2026-01-10T08:00:00Z',
author: {
'@type': 'Organization',
name: 'Acme Engineering Team',
},
publisher: {
'@type': 'Organization',
name: 'Acme Company',
logo: {
'@type': 'ImageObject',
url: 'https://acme.com/logo.png',
},
},
};
4. Sitelinks Search Box → Remove
// ❌ OLD: Sitelinks Search Box (discontinued)
const oldSchema = {
'@context': 'https://schema.org',
'@type': 'WebSite',
url: 'https://acme.com',
potentialAction: {
'@type': 'SearchAction',
target: {
'@type': 'EntryPoint',
urlTemplate: 'https://acme.com/search?q={search_term_string}',
},
'query-input': 'required name=search_term_string',
},
};
// ✅ NEW: Remove SearchAction, Google generates automatically
import { WebSite, WithContext } from 'schema-dts';
const newSchema: WithContext<WebSite> = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Acme Company',
url: 'https://acme.com',
description: 'Enterprise-grade development tools',
// Google will automatically generate sitelinks based on:
// 1. Site structure and navigation
// 2. Internal linking patterns
// 3. User engagement data
// No manual SearchAction needed
};
5. Dataset → Use Sparingly
// ⚠️ LIMITED: Dataset (only for Google Dataset Search)
import { Dataset, WithContext } from 'schema-dts';
const datasetSchema: WithContext<Dataset> = {
'@context': 'https://schema.org',
'@type': 'Dataset',
name: 'Weather Observations 2025',
description: 'Hourly weather data from 500+ meteorological stations across North America.',
url: 'https://acme.com/datasets/weather-2025',
// Dataset-specific properties
temporalCoverage: '2025-01-01/2025-12-31',
spatialCoverage: {
'@type': 'Place',
geo: {
'@type': 'GeoShape',
box: '18.0 -170.0 71.5 -66.0', // North America bounding box
},
},
// Only appears in https://datasetsearch.research.google.com/
// Not in general Google Search results
// Use only if targeting dataset-specific search tools
};
Audit Your Existing Schemas
Check for deprecated types:
# Search for deprecated schema types in your codebase
grep -r "PracticeProblem\|QAPage\|SpecialAnnouncement\|SearchAction\|'Dataset'" apps/web/app/
# Check all JSON-LD script tags
grep -r "application/ld+json" apps/web/app/ -A 20 | grep "@type"
Validation checklist:
- No
PracticeProblemtypes (useLearningResourceinstead) - No
QAPagetypes (useFAQPageinstead) - No
SpecialAnnouncementtypes (useNewsArticle/Articleinstead) - No
SearchActioninWebSitetypes (remove, Google auto-generates) -
Datasetonly used for dataset-specific tools (not general SEO)
When to Update
- Immediate: If using any of the 5 deprecated types
- Before deployment: Validate all new structured data against current Google support
- Monthly audit: Check Google Search Console for structured data errors
- After Schema.org updates: Review Schema.org releases quarterly
Testing Deprecated Schemas
# Test with Google Rich Results Test
# URL: https://search.google.com/test/rich-results
# Deprecated types will show warnings:
# ⚠️ "This feature is no longer eligible for rich results"
# ⚠️ "Google does not support this schema type"
# Validate with Schema.org validator
# URL: https://validator.schema.org/
# Note: Schema.org validator may still pass, but Google won't use it
JSON-LD Basics
Basic Implementation in Next.js
// app/page.tsx
export default function HomePage() {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Acme Company',
url: 'https://acme.com',
logo: 'https://acme.com/logo.png',
sameAs: [
'https://twitter.com/acmecompany',
'https://facebook.com/acmecompany',
'https://linkedin.com/company/acmecompany',
],
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<main>{/* Page content */}</main>
</>
);
}
XSS Security (CRITICAL)
Problem: JSON.stringify can create XSS vulnerabilities.
Solution: Sanitize < characters.
// ✅ SAFE: Replace < with unicode
function sanitizeJsonLd(obj: object) {
return JSON.stringify(obj).replace(/</g, '\\u003c');
}
// Usage:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: sanitizeJsonLd(jsonLd) }}
/>
// Alternative: Use serialize-javascript library
import serialize from 'serialize-javascript';
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: serialize(jsonLd) }}
/>
TypeScript Support (schema-dts)
Installation:
pnpm add schema-dts
Usage:
import { Organization, WithContext } from 'schema-dts';
const jsonLd: WithContext<Organization> = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Acme Company',
url: 'https://acme.com',
logo: 'https://acme.com/logo.png',
};
Benefits:
- Type checking for all Schema.org properties
- Autocomplete in IDE
- Prevents typos and invalid properties
Common Schema Types
Organization
Use For: Company homepage, about page.
import { Organization, WithContext } from 'schema-dts';
const jsonLd: WithContext<Organization> = {
'@context': 'https://schema.org',
'@type': 'Organization',
name: 'Acme Company',
alternateName: 'Acme',
url: 'https://acme.com',
logo: 'https://acme.com/logo.png',
description: 'Enterprise-grade development tools for modern teams.',
// Contact info
email: 'contact@acme.com',
telephone: '+1-555-123-4567',
// Address
address: {
'@type': 'PostalAddress',
streetAddress: '123 Main Street',
addressLocality: 'San Francisco',
addressRegion: 'CA',
postalCode: '94105',
addressCountry: 'US',
},
// Social profiles
sameAs: [
'https://twitter.com/acmecompany',
'https://facebook.com/acmecompany',
'https://linkedin.com/company/acmecompany',
'https://github.com/acmecompany',
'https://instagram.com/acmecompany',
],
// Founder
founder: {
'@type': 'Person',
name: 'John Doe',
},
// Founding date
foundingDate: '2020-01-15',
};
Article (Blog Posts)
Use For: Blog posts, news articles, editorial content.
import { Article, WithContext } from 'schema-dts';
const jsonLd: WithContext<Article> = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: 'How to Build a SaaS App in 2026',
description: 'Complete guide to building scalable SaaS applications with Next.js.',
// Images (required for rich results)
image: [
'https://acme.com/blog/saas-guide-1x1.jpg', // 1:1 aspect ratio
'https://acme.com/blog/saas-guide-4x3.jpg', // 4:3 aspect ratio
'https://acme.com/blog/saas-guide-16x9.jpg', // 16:9 aspect ratio
],
// Dates
datePublished: '2026-01-10T08:00:00+00:00',
dateModified: '2026-01-10T12:00:00+00:00',
// Author
author: {
'@type': 'Person',
name: 'Jane Smith',
url: 'https://acme.com/authors/jane-smith',
},
// Publisher (required)
publisher: {
'@type': 'Organization',
name: 'Acme Blog',
logo: {
'@type': 'ImageObject',
url: 'https://acme.com/logo.png',
},
},
// Article body (optional, but helpful)
articleBody: 'Full article text...',
// Word count (optional)
wordCount: 2500,
// URL
url: 'https://acme.com/blog/how-to-build-saas-app',
// Main entity of page
mainEntityOfPage: {
'@type': 'WebPage',
'@id': 'https://acme.com/blog/how-to-build-saas-app',
},
};
Product (E-commerce)
Use For: Product pages.
import { Product, WithContext } from 'schema-dts';
const jsonLd: WithContext<Product> = {
'@context': 'https://schema.org',
'@type': 'Product',
name: 'Premium Widget Pro',
description: 'The best widget on the market with advanced features.',
// Images (multiple recommended)
image: [
'https://acme.com/products/widget-1.jpg',
'https://acme.com/products/widget-2.jpg',
'https://acme.com/products/widget-3.jpg',
],
// SKU & Brand
sku: 'WIDGET-PRO-001',
mpn: 'WP-001', // Manufacturer Part Number
brand: {
'@type': 'Brand',
name: 'Acme',
},
// Offers (required for rich results)
offers: {
'@type': 'Offer',
url: 'https://acme.com/products/premium-widget-pro',
priceCurrency: 'USD',
price: 99.99,
priceValidUntil: '2026-12-31',
availability: 'https://schema.org/InStock', // or OutOfStock, PreOrder
seller: {
'@type': 'Organization',
name: 'Acme Store',
},
},
// Aggregate rating (if available)
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: 4.8,
reviewCount: 234,
bestRating: 5,
worstRating: 1,
},
// Reviews (optional)
review: [
{
'@type': 'Review',
reviewRating: {
'@type': 'Rating',
ratingValue: 5,
bestRating: 5,
},
author: {
'@type': 'Person',
name: 'John Customer',
},
reviewBody: 'Amazing product! Highly recommended.',
datePublished: '2026-01-05',
},
],
};
Recipe
Use For: Food recipes.
import { Recipe, WithContext } from 'schema-dts';
const jsonLd: WithContext<Recipe> = {
'@context': 'https://schema.org',
'@type': 'Recipe',
name: 'Chocolate Chip Cookies',
description: 'The best chocolate chip cookies you'll ever make!',
// Images (required)
image: ['https://acme.com/recipes/cookies.jpg'],
// Author
author: {
'@type': 'Person',
name: 'Chef Jane',
},
// Dates
datePublished: '2026-01-10',
// Time
prepTime: 'PT15M', // 15 minutes (ISO 8601 duration)
cookTime: 'PT15M', // 15 minutes
totalTime: 'PT30M', // 30 minutes
// Yield
recipeYield: '24 cookies',
// Category & Cuisine
recipeCategory: 'Dessert',
recipeCuisine: 'American',
// Keywords
keywords: 'chocolate chip, cookies, dessert, baking',
// Nutrition (optional)
nutrition: {
'@type': 'NutritionInformation',
calories: '320 calories',
fatContent: '16g',
sugarContent: '24g',
},
// Ingredients (required)
recipeIngredient: [
'2 1/4 cups all-purpose flour',
'1 tsp baking soda',
'1 tsp salt',
'1 cup butter, softened',
'3/4 cup granulated sugar',
'3/4 cup brown sugar',
'2 large eggs',
'2 tsp vanilla extract',
'2 cups chocolate chips',
],
// Instructions (required)
recipeInstructions: [
{
'@type': 'HowToStep',
text: 'Preheat oven to 375°F (190°C).',
},
{
'@type': 'HowToStep',
text: 'Combine flour, baking soda, and salt in a bowl.',
},
{
'@type': 'HowToStep',
text: 'Beat butter and sugars until creamy. Add eggs and vanilla.',
},
{
'@type': 'HowToStep',
text: 'Gradually mix in flour mixture. Stir in chocolate chips.',
},
{
'@type': 'HowToStep',
text: 'Drop spoonfuls onto baking sheet and bake 9-11 minutes.',
},
],
// Aggregate rating
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: 4.9,
reviewCount: 156,
},
};
FAQ Page
Use For: FAQ sections, Q&A pages.
import { FAQPage, WithContext } from 'schema-dts';
const jsonLd: WithContext<FAQPage> = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: [
{
'@type': 'Question',
name: 'What is your return policy?',
acceptedAnswer: {
'@type': 'Answer',
text: 'We offer a 30-day money-back guarantee on all products. If you're not satisfied, contact us for a full refund.',
},
},
{
'@type': 'Question',
name: 'How long does shipping take?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Standard shipping takes 5-7 business days. Express shipping (2-3 days) is available for an additional fee.',
},
},
{
'@type': 'Question',
name: 'Do you ship internationally?',
acceptedAnswer: {
'@type': 'Answer',
text: 'Yes, we ship to over 50 countries worldwide. International shipping times vary by destination.',
},
},
],
};
Event
Use For: Events, conferences, webinars.
import { Event, WithContext } from 'schema-dts';
const jsonLd: WithContext<Event> = {
'@context': 'https://schema.org',
'@type': 'Event',
name: 'Next.js Conf 2026',
description: 'The official Next.js conference featuring talks from the core team and community.',
// Image
image: ['https://acme.com/events/nextjs-conf.jpg'],
// Start & End dates (ISO 8601 format)
startDate: '2026-10-25T09:00:00-07:00',
endDate: '2026-10-26T18:00:00-07:00',
// Status
eventStatus: 'https://schema.org/EventScheduled', // or EventCancelled, EventPostponed
eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode', // or OnlineEventAttendanceMode, MixedEventAttendanceMode
// Location
location: {
'@type': 'Place',
name: 'Moscone Center',
address: {
'@type': 'PostalAddress',
streetAddress: '747 Howard St',
addressLocality: 'San Francisco',
addressRegion: 'CA',
postalCode: '94103',
addressCountry: 'US',
},
},
// Organizer
organizer: {
'@type': 'Organization',
name: 'Vercel',
url: 'https://vercel.com',
},
// Offers (tickets)
offers: {
'@type': 'Offer',
url: 'https://acme.com/events/nextjs-conf/tickets',
price: 299,
priceCurrency: 'USD',
availability: 'https://schema.org/InStock',
validFrom: '2026-01-10T09:00:00-08:00',
},
// Performer (speakers)
performer: [
{
'@type': 'Person',
name: 'Lee Robinson',
},
{
'@type': 'Person',
name: 'Guillermo Rauch',
},
],
};
Local Business
Use For: Physical business locations (restaurants, stores, offices).
import { LocalBusiness, WithContext } from 'schema-dts';
const jsonLd: WithContext<LocalBusiness> = {
'@context': 'https://schema.org',
'@type': 'Restaurant', // or Store, LocalBusiness, etc.
name: 'Acme Cafe',
description: 'Cozy cafe serving artisan coffee and homemade pastries.',
// Images
image: ['https://acme.com/cafe/interior.jpg'],
// Contact
telephone: '+1-555-987-6543',
email: 'info@acmecafe.com',
url: 'https://acmecafe.com',
// Address
address: {
'@type': 'PostalAddress',
streetAddress: '456 Market Street',
addressLocality: 'San Francisco',
addressRegion: 'CA',
postalCode: '94102',
addressCountry: 'US',
},
// Geo coordinates (for maps)
geo: {
'@type': 'GeoCoordinates',
latitude: 37.7749,
longitude: -122.4194,
},
// Opening hours
openingHoursSpecification: [
{
'@type': 'OpeningHoursSpecification',
dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
opens: '07:00',
closes: '19:00',
},
{
'@type': 'OpeningHoursSpecification',
dayOfWeek: ['Saturday', 'Sunday'],
opens: '08:00',
closes: '20:00',
},
],
// Price range
priceRange: '$$',
// Cuisine (for restaurants)
servesCuisine: ['American', 'Coffee'],
// Aggregate rating
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: 4.7,
reviewCount: 89,
},
};
Breadcrumb
Use For: Breadcrumb navigation.
import { BreadcrumbList, WithContext } from 'schema-dts';
const jsonLd: WithContext<BreadcrumbList> = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: [
{
'@type': 'ListItem',
position: 1,
name: 'Home',
item: 'https://acme.com',
},
{
'@type': 'ListItem',
position: 2,
name: 'Blog',
item: 'https://acme.com/blog',
},
{
'@type': 'ListItem',
position: 3,
name: 'How to Build a SaaS App',
item: 'https://acme.com/blog/how-to-build-saas-app',
},
],
};
Dynamic Structured Data
Blog Post Example
// app/blog/[slug]/page.tsx
import { Article, WithContext } from 'schema-dts';
export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
const post = await getBlogPost(slug);
const jsonLd: WithContext<Article> = {
'@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,
url: `https://acme.com/authors/${post.author.slug}`,
},
publisher: {
'@type': 'Organization',
name: 'Acme Blog',
logo: {
'@type': 'ImageObject',
url: 'https://acme.com/logo.png',
},
},
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<article>{/* Post content */}</article>
</>
);
}
Product Page Example
// app/products/[id]/page.tsx
import { Product, WithContext } from 'schema-dts';
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = await getProduct(id);
const jsonLd: WithContext<Product> = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
image: product.images,
sku: product.sku,
brand: {
'@type': 'Brand',
name: product.brand,
},
offers: {
'@type': 'Offer',
url: `https://acme.com/products/${id}`,
priceCurrency: 'USD',
price: product.price,
availability: product.inStock
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
},
aggregateRating: product.reviews?.count > 0 ? {
'@type': 'AggregateRating',
ratingValue: product.reviews.average,
reviewCount: product.reviews.count,
} : undefined,
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<main>{/* Product content */}</main>
</>
);
}
Reusable Component Pattern
StructuredData Component
// components/structured-data.tsx
interface StructuredDataProps {
data: object;
}
export function StructuredData({ data }: StructuredDataProps) {
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(data).replace(/</g, '\\u003c'),
}}
/>
);
}
// Usage:
import { StructuredData } from '@/components/structured-data';
export default function Page() {
const jsonLd = { '@context': 'https://schema.org', '@type': 'Organization', ... };
return (
<>
<StructuredData data={jsonLd} />
<main>Content</main>
</>
);
}
Multiple Schemas on One Page
Allowed: One page can have multiple structured data blocks.
export default function BlogPostPage() {
const article = { '@context': 'https://schema.org', '@type': 'Article', ... };
const breadcrumb = { '@context': 'https://schema.org', '@type': 'BreadcrumbList', ... };
const faq = { '@context': 'https://schema.org', '@type': 'FAQPage', ... };
return (
<>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(article) }} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumb) }} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(faq) }} />
<article>{/* Content */}</article>
</>
);
}
Validation & Testing
Google Rich Results Test
URL: https://search.google.com/test/rich-results
Use For:
- Test if page is eligible for rich results
- Preview how rich results will appear
- Identify errors in structured data
- See which rich result features are detected
How to Use:
- Enter URL or paste code
- Click "Test URL" or "Test Code"
- Review detected features
- Fix any errors or warnings
- Re-test after changes
Schema Markup Validator
URL: https://validator.schema.org/
Use For:
- Validate JSON-LD syntax
- Check Schema.org compliance
- Identify property errors
- Verify schema structure
How to Use:
- Paste JSON-LD code
- Click "Run Test"
- Review errors and warnings
- Fix issues
- Re-validate
View Source Check
Quick Validation:
# View page source
curl https://your-site.com/page | grep 'application/ld+json'
# Or in browser: view-source:https://your-site.com/page
# Look for <script type="application/ld+json">
Common Issues
Issue: Rich Results Not Showing
Possible Causes:
- Structured data has errors
- Missing required properties
- Google hasn't re-crawled page
- Page doesn't meet quality guidelines
Solution:
- Use Rich Results Test to validate
- Fix all errors and warnings
- Request re-indexing in Google Search Console
- Ensure content quality meets Google standards
- Wait (rich results can take days/weeks to appear)
Issue: Invalid JSON-LD Syntax
Problem: JSON-LD contains syntax errors.
Common Mistakes:
// ❌ Missing quotes on property names
{ @context: 'https://schema.org' }
// ✅ Correct
{ '@context': 'https://schema.org' }
// ❌ Trailing comma
{ name: 'Acme', url: 'https://acme.com', }
// ✅ Correct
{ name: 'Acme', url: 'https://acme.com' }
Issue: XSS Vulnerability
Problem: User-generated content in JSON-LD creates XSS risk.
Solution: Sanitize < characters.
// ✅ Safe
function sanitizeJsonLd(obj: object) {
return JSON.stringify(obj).replace(/</g, '\\u003c');
}
Best Practices
Design Best Practices
- Use most specific schema type (Article > CreativeWork)
- Include all required properties for rich results
- Add images (1200x630 px, multiple aspect ratios)
- Use absolute URLs (not relative)
- Include dates in ISO 8601 format (2026-01-10T08:00:00Z)
- Nest related entities (Author within Article)
- Match structured data to visible content (no hidden data)
Content Best Practices
- Structured data must reflect actual page content
- Don't mark up content that isn't visible on page
- Keep descriptions accurate and concise
- Use proper date formats (ISO 8601)
- Include multiple images when available
- Set appropriate availability status (InStock/OutOfStock)
- Update dates when content changes (dateModified)
Code Best Practices
- Use TypeScript with schema-dts for type safety
- Sanitize JSON-LD to prevent XSS
- Place
<script>tag in body (not head required) - Use reusable StructuredData component
- Generate dynamically for dynamic pages
- Test with validators before deploying
- Monitor Search Console for structured data errors
Related Skills
nextjs-seo-metadata- Core Next.js metadata APIopen-graph-twitter- Social media metadataseo-validation-testing- Testing and validation tools
Sources & References
Official Documentation
- Guides: JSON-LD | Next.js
- Schema.org - Official schema vocabulary
- Google Search Central: Structured Data
Implementation Guides
- Implementing JSON-LD in Next.js for SEO
- Add Structured Data with JSON-LD for better SEO
- How to add Schema.org to a Next.js 13+ website
- Implementing JSON-LD in Next.js 13/14 with TypeScript
Validation Tools
- Rich Results Test - Google
- Schema Markup Validator
- Google Search Console - Monitor structured data errors
Libraries
- schema-dts - TypeScript types for Schema.org
- serialize-javascript - Safe JSON serialization
Changelog
v2.0.0 (2026-01-12)
- CRITICAL: Added Google Schema.org deprecations warning (January 2026)
- Google discontinued support for 5 Schema.org types in January 2026
- Added deprecation table with status, replacements, and impact
- Documented all 5 deprecated types:
- Practice Problem → Use
LearningResourceorQuiz - Dataset → Limited to Google Dataset Search only
- Sitelinks Search Box → Google generates automatically (remove manual implementation)
- SpecialAnnouncement → Use
NewsArticleorArticle - Q&A (QAPage) → Use
FAQPageinstead
- Practice Problem → Use
- Added comprehensive migration guides with TypeScript examples for each deprecated type
- Added audit section: Bash commands to check for deprecated types in codebase
- Added validation checklist: 5-point checklist for deprecated schemas
- Added testing section: How deprecated types show warnings in Google Rich Results Test
- Added "When to Update" section: Timeline for removing deprecated schemas
- Updated skill description with deprecation warnings
- Impact: Prevents using discontinued schema types that won't generate rich results, ensures compatibility with Google Search
v1.0.0 (Initial)
- Core JSON-LD implementation patterns
- Common schema types (Organization, Article, Product, Recipe, FAQ, Event, LocalBusiness, Breadcrumb)
- TypeScript support with schema-dts
- XSS security patterns
- Dynamic structured data for Next.js
- Validation and testing tools
Last Updated: January 12, 2026 Version: 2.0.0 Knowledge Base: Schema.org vocabulary + Next.js 16 JSON-LD patterns + January 2026 Google deprecations Confidence Level: High (based on official Schema.org and Google documentation)