Next.js Schema.org Cheat Sheet

Basic JSON-LD Implementation

Article Schema

// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
    const post = await getPost(params.slug);
    
    const articleSchema = {
        '@context': 'https://schema.org',
        '@type': 'Article',
        headline: post.title,
        description: post.excerpt,
        image: post.featuredImage,
        author: {
            '@type': 'Person',
            name: post.author.name,
            url: post.author.url,
        },
        publisher: {
            '@type': 'Organization',
            name: 'My Website',
            logo: {
                '@type': 'ImageObject',
                url: 'https://example.com/logo.png',
            },
        },
        datePublished: post.publishedAt,
        dateModified: post.updatedAt,
        mainEntityOfPage: {
            '@type': 'WebPage',
            '@id': `https://example.com/blog/${params.slug}`,
        },
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
            />
            <article>
                <h1>{post.title}</h1>
                <div>{post.content}</div>
            </article>
        </>
    );
}

Organization Schema

// app/layout.js
export default function RootLayout({ children }) {
    const organizationSchema = {
        '@context': 'https://schema.org',
        '@type': 'Organization',
        name: 'My Company',
        url: 'https://example.com',
        logo: 'https://example.com/logo.png',
        sameAs: [
            'https://twitter.com/mycompany',
            'https://linkedin.com/company/mycompany',
            'https://facebook.com/mycompany',
        ],
        contactPoint: {
            '@type': 'ContactPoint',
            telephone: '+1-555-123-4567',
            contactType: 'customer service',
            email: 'contact@example.com',
        },
        address: {
            '@type': 'PostalAddress',
            streetAddress: '123 Main St',
            addressLocality: 'City',
            addressRegion: 'State',
            postalCode: '12345',
            addressCountry: 'US',
        },
    };
    
    return (
        <html lang="en">
            <head>
                <script
                    type="application/ld+json"
                    dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema) }}
                />
            </head>
            <body>{children}</body>
        </html>
    );
}

E-commerce Schemas

Product Schema

// app/products/[id]/page.js
export default function ProductPage({ params }) {
    const product = await getProduct(params.id);
    
    const productSchema = {
        '@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',
            price: product.price,
            priceCurrency: 'USD',
            availability: product.inStock 
                ? 'https://schema.org/InStock' 
                : 'https://schema.org/OutOfStock',
            url: `https://example.com/products/${params.id}`,
            seller: {
                '@type': 'Organization',
                name: 'My Store',
            },
        },
        aggregateRating: product.rating ? {
            '@type': 'AggregateRating',
            ratingValue: product.rating.average,
            reviewCount: product.rating.count,
        } : undefined,
        review: product.reviews?.map(review => ({
            '@type': 'Review',
            author: {
                '@type': 'Person',
                name: review.author,
            },
            reviewRating: {
                '@type': 'Rating',
                ratingValue: review.rating,
            },
            reviewBody: review.content,
        })),
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(productSchema) }}
            />
            <div>
                <h1>{product.name}</h1>
                <p>{product.description}</p>
            </div>
        </>
    );
}

Breadcrumb Schema

// components/Breadcrumbs.jsx
export default function Breadcrumbs({ items }) {
    const breadcrumbSchema = {
        '@context': 'https://schema.org',
        '@type': 'BreadcrumbList',
        itemListElement: items.map((item, index) => ({
            '@type': 'ListItem',
            position: index + 1,
            name: item.name,
            item: item.url,
        })),
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
            />
            <nav aria-label="Breadcrumb">
                {items.map((item, index) => (
                    <span key={item.url}>
                        <a href={item.url}>{item.name}</a>
                        {index < items.length - 1 && ' > '}
                    </span>
                ))}
            </nav>
        </>
    );
}

Local Business Schemas

Local Business Schema

// app/location/page.js
export default function LocationPage() {
    const businessSchema = {
        '@context': 'https://schema.org',
        '@type': 'LocalBusiness',
        name: 'My Restaurant',
        image: 'https://example.com/restaurant.jpg',
        address: {
            '@type': 'PostalAddress',
            streetAddress: '123 Main St',
            addressLocality: 'City',
            addressRegion: 'State',
            postalCode: '12345',
            addressCountry: 'US',
        },
        geo: {
            '@type': 'GeoCoordinates',
            latitude: 40.7128,
            longitude: -74.0060,
        },
        url: 'https://example.com',
        telephone: '+1-555-123-4567',
        priceRange: '$$',
        openingHoursSpecification: [
            {
                '@type': 'OpeningHoursSpecification',
                dayOfWeek: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
                opens: '09:00',
                closes: '17:00',
            },
            {
                '@type': 'OpeningHoursSpecification',
                dayOfWeek: ['Saturday'],
                opens: '10:00',
                closes: '16:00',
            },
        ],
        servesCuisine: ['Italian', 'Mediterranean'],
        hasMenu: 'https://example.com/menu',
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(businessSchema) }}
            />
            <div>
                <h1>Visit Our Location</h1>
                {/* Location content */}
            </div>
        </>
    );
}

FAQ Schema

FAQ Schema

// app/faq/page.js
export default function FAQPage() {
    const faqSchema = {
        '@context': 'https://schema.org',
        '@type': 'FAQPage',
        mainEntity: [
            {
                '@type': 'Question',
                name: 'What is your return policy?',
                acceptedAnswer: {
                    '@type': 'Answer',
                    text: 'We offer a 30-day return policy for all unused items.',
                },
            },
            {
                '@type': 'Question',
                name: 'Do you ship internationally?',
                acceptedAnswer: {
                    '@type': 'Answer',
                    text: 'Yes, we ship to most countries worldwide.',
                },
            },
        ],
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(faqSchema) }}
            />
            <div>
                <h1>Frequently Asked Questions</h1>
                {/* FAQ content */}
            </div>
        </>
    );
}

Event Schema

Event Schema

// app/events/[id]/page.js
export default function EventPage({ params }) {
    const event = await getEvent(params.id);
    
    const eventSchema = {
        '@context': 'https://schema.org',
        '@type': 'Event',
        name: event.title,
        description: event.description,
        startDate: event.startDate,
        endDate: event.endDate,
        location: {
            '@type': 'Place',
            name: event.venue.name,
            address: {
                '@type': 'PostalAddress',
                streetAddress: event.venue.address,
                addressLocality: event.venue.city,
                addressRegion: event.venue.state,
                postalCode: event.venue.zip,
            },
        },
        organizer: {
            '@type': 'Organization',
            name: event.organizer.name,
            url: event.organizer.url,
        },
        offers: {
            '@type': 'Offer',
            price: event.price,
            priceCurrency: 'USD',
            availability: event.ticketsAvailable 
                ? 'https://schema.org/InStock' 
                : 'https://schema.org/OutOfStock',
            url: `https://example.com/events/${params.id}/tickets`,
        },
        performer: event.performers?.map(performer => ({
            '@type': 'Person',
            name: performer.name,
        })),
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(eventSchema) }}
            />
            <div>
                <h1>{event.title}</h1>
                <p>{event.description}</p>
            </div>
        </>
    );
}

Person Schema

Person Schema

// app/team/[slug]/page.js
export default function TeamMemberPage({ params }) {
    const member = await getTeamMember(params.slug);
    
    const personSchema = {
        '@context': 'https://schema.org',
        '@type': 'Person',
        name: member.name,
        jobTitle: member.title,
        worksFor: {
            '@type': 'Organization',
            name: 'My Company',
        },
        description: member.bio,
        image: member.photo,
        url: `https://example.com/team/${params.slug}`,
        sameAs: [
            member.linkedin,
            member.twitter,
            member.github,
        ].filter(Boolean),
        knowsAbout: member.skills,
        alumniOf: member.education?.map(edu => ({
            '@type': 'Organization',
            name: edu.school,
        })),
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(personSchema) }}
            />
            <div>
                <h1>{member.name}</h1>
                <p>{member.title}</p>
                <p>{member.bio}</p>
            </div>
        </>
    );
}

WebPage Schema

WebPage Schema

// app/about/page.js
export default function AboutPage() {
    const webpageSchema = {
        '@context': 'https://schema.org',
        '@type': 'WebPage',
        name: 'About Us',
        description: 'Learn more about our company and mission',
        url: 'https://example.com/about',
        isPartOf: {
            '@type': 'WebSite',
            name: 'My Website',
            url: 'https://example.com',
        },
        about: {
            '@type': 'Organization',
            name: 'My Company',
        },
        mainEntity: {
            '@type': 'Organization',
            name: 'My Company',
            description: 'A leading company in our industry',
            foundingDate: '2020-01-01',
            numberOfEmployees: '50-100',
        },
    };
    
    return (
        <>
            <script
                type="application/ld+json"
                dangerouslySetInnerHTML={{ __html: JSON.stringify(webpageSchema) }}
            />
            <div>
                <h1>About Us</h1>
                {/* About content */}
            </div>
        </>
    );
}

Utility Functions

Schema Helper Functions

// lib/schema.js
export function createArticleSchema(post) {
    return {
        '@context': 'https://schema.org',
        '@type': 'Article',
        headline: post.title,
        description: post.excerpt,
        image: post.featuredImage,
        author: {
            '@type': 'Person',
            name: post.author.name,
        },
        publisher: {
            '@type': 'Organization',
            name: 'My Website',
            logo: {
                '@type': 'ImageObject',
                url: 'https://example.com/logo.png',
            },
        },
        datePublished: post.publishedAt,
        dateModified: post.updatedAt,
    };
}

export function createProductSchema(product) {
    return {
        '@context': 'https://schema.org',
        '@type': 'Product',
        name: product.name,
        description: product.description,
        image: product.images,
        offers: {
            '@type': 'Offer',
            price: product.price,
            priceCurrency: 'USD',
            availability: product.inStock 
                ? 'https://schema.org/InStock' 
                : 'https://schema.org/OutOfStock',
        },
    };
}

export function createBreadcrumbSchema(items) {
    return {
        '@context': 'https://schema.org',
        '@type': 'BreadcrumbList',
        itemListElement: items.map((item, index) => ({
            '@type': 'ListItem',
            position: index + 1,
            name: item.name,
            item: item.url,
        })),
    };
}

Schema Component

// components/SchemaScript.jsx
export default function SchemaScript({ schema }) {
    return (
        <script
            type="application/ld+json"
            dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
        />
    );
}

// Usage
import SchemaScript from '@/components/SchemaScript';
import { createArticleSchema } from '@/lib/schema';

export default function BlogPost({ post }) {
    const schema = createArticleSchema(post);
    
    return (
        <>
            <SchemaScript schema={schema} />
            <article>
                <h1>{post.title}</h1>
                <div>{post.content}</div>
            </article>
        </>
    );
}

Validation and Testing

Schema Validation

// lib/validateSchema.js
export function validateSchema(schema) {
    const errors = [];
    
    // Check required fields
    if (!schema['@context']) {
        errors.push('Missing @context');
    }
    
    if (!schema['@type']) {
        errors.push('Missing @type');
    }
    
    // Validate specific types
    if (schema['@type'] === 'Article') {
        if (!schema.headline) {
            errors.push('Article missing headline');
        }
        if (!schema.author) {
            errors.push('Article missing author');
        }
    }
    
    if (schema['@type'] === 'Product') {
        if (!schema.name) {
            errors.push('Product missing name');
        }
        if (!schema.offers) {
            errors.push('Product missing offers');
        }
    }
    
    return errors;
}

// Usage
const schema = createArticleSchema(post);
const errors = validateSchema(schema);

if (errors.length > 0) {
    console.warn('Schema validation errors:', errors);
}

Google Rich Results Testing

// lib/testRichResults.js
export async function testRichResults(url, schema) {
    const testUrl = 'https://search.google.com/test/rich-results';
    const schemaString = JSON.stringify(schema);
    
    console.log('Test your schema at:', testUrl);
    console.log('Schema:', schemaString);
    
    // You can also use the Google Rich Results API
    const response = await fetch('https://searchconsole.googleapis.com/v1/urlTestingTools/mobileFriendlyTest:run', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            url: url,
            requestScreenshot: false,
        }),
    });
    
    return response.json();
}

Automation Strategies

Dynamic Schema Generation

// lib/generateSchema.js
export function generateSchemaForPage(pageType, data) {
    switch (pageType) {
        case 'article':
            return createArticleSchema(data);
        case 'product':
            return createProductSchema(data);
        case 'person':
            return createPersonSchema(data);
        case 'event':
            return createEventSchema(data);
        case 'organization':
            return createOrganizationSchema(data);
        default:
            return null;
    }
}

// Usage in page
export default function DynamicPage({ pageType, data }) {
    const schema = generateSchemaForPage(pageType, data);
    
    return (
        <>
            {schema && <SchemaScript schema={schema} />}
            <div>
                {/* Page content */}
            </div>
        </>
    );
}

Schema Middleware

// middleware.js
import { NextResponse } from 'next/server';

export function middleware(request) {
    const response = NextResponse.next();
    
    // Add schema.org headers for specific routes
    if (request.nextUrl.pathname.startsWith('/blog/')) {
        response.headers.set('X-Schema-Type', 'Article');
    }
    
    if (request.nextUrl.pathname.startsWith('/products/')) {
        response.headers.set('X-Schema-Type', 'Product');
    }
    
    return response;
}

Best Practices

Performance Optimization

// Optimize schema loading
export default function OptimizedSchema({ schema }) {
    // Only load schema on client if needed
    const [shouldLoadSchema, setShouldLoadSchema] = useState(false);
    
    useEffect(() => {
        // Load schema after page load for better performance
        const timer = setTimeout(() => {
            setShouldLoadSchema(true);
        }, 100);
        
        return () => clearTimeout(timer);
    }, []);
    
    return (
        <>
            {shouldLoadSchema && <SchemaScript schema={schema} />}
            {/* Page content */}
        </>
    );
}

Error Handling

// lib/safeSchema.js
export function createSafeSchema(schema) {
    try {
        // Remove undefined values
        const cleanSchema = JSON.parse(JSON.stringify(schema));
        
        // Validate required fields
        const errors = validateSchema(cleanSchema);
        
        if (errors.length > 0) {
            console.warn('Schema validation errors:', errors);
            return null;
        }
        
        return cleanSchema;
    } catch (error) {
        console.error('Schema creation error:', error);
        return null;
    }
}

SEO Integration

// lib/seoSchema.js
export function createSEOSchema(pageData) {
    const baseSchema = {
        '@context': 'https://schema.org',
        '@type': 'WebPage',
        name: pageData.title,
        description: pageData.description,
        url: pageData.url,
    };
    
    // Add specific schema based on page type
    if (pageData.type === 'article') {
        return {
            ...baseSchema,
            '@type': 'Article',
            headline: pageData.title,
            author: pageData.author,
            datePublished: pageData.publishedAt,
        };
    }
    
    return baseSchema;
}