Skip to main content
Back to Blog
Tutorials
3 min read
November 21, 2024

How to Add JSON-LD Structured Data to Next.js

Add JSON-LD structured data to your Next.js pages for rich search results including articles, FAQs, products, and more.

Ryel Banfield

Founder & Lead Developer

Structured data helps search engines understand your content and display rich results. Here is how to implement it.

Step 1: JSON-LD Component

// components/JsonLd.tsx
export function JsonLd({ data }: { data: Record<string, unknown> }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
    />
  );
}

Step 2: Organization Schema

Add to your root layout or homepage:

// app/layout.tsx
import { JsonLd } from "@/components/JsonLd";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <JsonLd
          data={{
            "@context": "https://schema.org",
            "@type": "Organization",
            name: "RCB Software",
            url: "https://rcbsoftware.com",
            logo: "https://rcbsoftware.com/logos/logo.png",
            description:
              "Custom web design, development, and software solutions for businesses worldwide.",
            foundingDate: "2020",
            sameAs: [
              "https://twitter.com/rcbsoftware",
              "https://linkedin.com/company/rcbsoftware",
              "https://github.com/rcbsoftware",
            ],
            contactPoint: {
              "@type": "ContactPoint",
              contactType: "customer service",
              url: "https://rcbsoftware.com/contact",
            },
          }}
        />
        {children}
      </body>
    </html>
  );
}

Step 3: Article Schema

// app/blog/[slug]/page.tsx
import { JsonLd } from "@/components/JsonLd";

interface BlogPost {
  title: string;
  slug: string;
  excerpt: string;
  date: string;
  author: string;
  image?: string;
  modifiedDate?: string;
}

function ArticleJsonLd({ post }: { post: BlogPost }) {
  return (
    <JsonLd
      data={{
        "@context": "https://schema.org",
        "@type": "Article",
        headline: post.title,
        description: post.excerpt,
        author: {
          "@type": "Person",
          name: post.author,
        },
        publisher: {
          "@type": "Organization",
          name: "RCB Software",
          logo: {
            "@type": "ImageObject",
            url: "https://rcbsoftware.com/logos/logo.png",
          },
        },
        datePublished: post.date,
        dateModified: post.modifiedDate ?? post.date,
        mainEntityOfPage: {
          "@type": "WebPage",
          "@id": `https://rcbsoftware.com/blog/${post.slug}`,
        },
        image: post.image ?? "https://rcbsoftware.com/images/og-default.png",
      }}
    />
  );
}

export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  const post = await getPost(slug); // Your data fetching logic

  return (
    <article>
      <ArticleJsonLd post={post} />
      <h1>{post.title}</h1>
      {/* Post content */}
    </article>
  );
}

Step 4: FAQ Schema

// components/FaqJsonLd.tsx
import { JsonLd } from "./JsonLd";

interface FAQ {
  question: string;
  answer: string;
}

export function FaqJsonLd({ faqs }: { faqs: FAQ[] }) {
  return (
    <JsonLd
      data={{
        "@context": "https://schema.org",
        "@type": "FAQPage",
        mainEntity: faqs.map((faq) => ({
          "@type": "Question",
          name: faq.question,
          acceptedAnswer: {
            "@type": "Answer",
            text: faq.answer,
          },
        })),
      }}
    />
  );
}

Usage:

<FaqJsonLd
  faqs={[
    {
      question: "How long does it take to build a website?",
      answer: "Most projects take 4-8 weeks from design to launch.",
    },
    {
      question: "Do you offer ongoing maintenance?",
      answer: "Yes, we offer monthly maintenance plans.",
    },
  ]}
/>

Step 5: Breadcrumb Schema

// components/BreadcrumbJsonLd.tsx
import { JsonLd } from "./JsonLd";

interface BreadcrumbItem {
  name: string;
  url: string;
}

export function BreadcrumbJsonLd({ items }: { items: BreadcrumbItem[] }) {
  return (
    <JsonLd
      data={{
        "@context": "https://schema.org",
        "@type": "BreadcrumbList",
        itemListElement: items.map((item, index) => ({
          "@type": "ListItem",
          position: index + 1,
          name: item.name,
          item: item.url,
        })),
      }}
    />
  );
}

Step 6: Product Schema

// components/ProductJsonLd.tsx
import { JsonLd } from "./JsonLd";

interface ProductData {
  name: string;
  description: string;
  image: string;
  price: number;
  currency: string;
  availability: "InStock" | "OutOfStock" | "PreOrder";
  rating?: { value: number; count: number };
  brand?: string;
  sku?: string;
  url: string;
}

export function ProductJsonLd({ product }: { product: ProductData }) {
  return (
    <JsonLd
      data={{
        "@context": "https://schema.org",
        "@type": "Product",
        name: product.name,
        description: product.description,
        image: product.image,
        brand: product.brand
          ? { "@type": "Brand", name: product.brand }
          : undefined,
        sku: product.sku,
        offers: {
          "@type": "Offer",
          price: product.price,
          priceCurrency: product.currency,
          availability: `https://schema.org/${product.availability}`,
          url: product.url,
        },
        aggregateRating: product.rating
          ? {
              "@type": "AggregateRating",
              ratingValue: product.rating.value,
              reviewCount: product.rating.count,
            }
          : undefined,
      }}
    />
  );
}

Step 7: Service Schema

// components/ServiceJsonLd.tsx
import { JsonLd } from "./JsonLd";

interface ServiceData {
  name: string;
  description: string;
  provider: string;
  areaServed?: string;
  url: string;
}

export function ServiceJsonLd({ service }: { service: ServiceData }) {
  return (
    <JsonLd
      data={{
        "@context": "https://schema.org",
        "@type": "Service",
        name: service.name,
        description: service.description,
        provider: {
          "@type": "Organization",
          name: service.provider,
        },
        areaServed: service.areaServed ?? "Worldwide",
        url: service.url,
      }}
    />
  );
}

Step 8: Local Business Schema

// components/LocalBusinessJsonLd.tsx
import { JsonLd } from "./JsonLd";

export function LocalBusinessJsonLd() {
  return (
    <JsonLd
      data={{
        "@context": "https://schema.org",
        "@type": "ProfessionalService",
        name: "RCB Software",
        url: "https://rcbsoftware.com",
        logo: "https://rcbsoftware.com/logos/logo.png",
        description: "Custom web and software development services",
        priceRange: "$$",
        address: {
          "@type": "PostalAddress",
          addressLocality: "Worldwide",
          addressCountry: "US",
        },
        openingHoursSpecification: {
          "@type": "OpeningHoursSpecification",
          dayOfWeek: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
          opens: "09:00",
          closes: "17:00",
        },
      }}
    />
  );
}

Step 9: Testing

Validate your structured data with:

  1. Google Rich Results Test: https://search.google.com/test/rich-results
  2. Schema Markup Validator: https://validator.schema.org/

Best Practices

  • Use one @type per JSON-LD block for clarity
  • Include required properties for each schema type
  • Keep data accurate and consistent with visible page content
  • Test with Google Rich Results Test before deploying
  • Monitor Google Search Console for structured data errors

Need SEO Optimization?

We implement technical SEO including structured data to help your pages stand out in search results. Contact us to get started.

JSON-LDstructured dataschema.orgSEONext.jstutorial

Ready to Start Your Project?

RCB Software builds world-class websites and applications for businesses worldwide.

Get in Touch

Related Articles