Back to blog

Published Updated

How to Add Schema Correctly in Next.js MDX Blogs

By Tushar ChoudharySchema • "Next.js • "MDX • "Technical SEO • "Structured Data • "Blog SEO • "JSON-LD

Implement accurate Article JSON-LD for Next.js MDX blogs with canonical URLs, author entities, dates, images, sanitization, validation and build checks.

How to Add Schema Correctly in Next.js MDX Blogs

The safest way to add schema to a Next.js MDX blog is to generate one accurate BlogPosting or Article JSON-LD object from the same validated post data used for the visible page. The structured data URL, canonical, Open Graph URL, dates, author, headline, description, and image should agree. Do not create markup for reviews, FAQs, prices, authors, or business claims that are absent or unverifiable on the page.

This guide covers a practical App Router pattern for file-based MDX blogs, including data normalization, safe serialization, dynamic routes, validation, and common build failures.

Author and Editorial Review

By Tushar C. (Founder, VASUYASHII). Reviewed by VASUYASHII Editorial against the current Next.js App Router and Google Search structured-data guidance available on July 20, 2026. Adapt code to your project's Next.js version, content parser, TypeScript rules, and deployment model.

Quick Answer

Use this pipeline:

  1. parse and validate MDX frontmatter;
  2. build the final canonical URL from one site origin and validated slug;
  3. convert relative image paths to absolute URLs;
  4. map the visible author to a real Person or Organization entity;
  5. use the original publish date and meaningful modification date;
  6. render JSON-LD in the route's server-rendered output;
  7. escape unsafe < characters during serialization;
  8. test generated HTML and Google's Rich Results Test; and
  9. monitor deployed pages in Search Console.

Google recommends JSON-LD for most implementations, but valid markup does not guarantee a rich result. The structured data must represent visible page content and follow the relevant feature guidelines.

What Schema Does and Does Not Do

Structured data gives machines explicit clues about a page and its entities. For a blog post, it can clarify the article headline, author, dates, publisher, primary image, and canonical page. It does not replace:

  • a clear title and H1;
  • useful visible content;
  • canonical consistency;
  • crawl access;
  • internal links;
  • author credibility; or
  • content quality.

Google's general structured data guidelines state that markup should be original, relevant, visible to readers, accurate, and not misleading. A technically valid JSON object can still be ineligible if it describes content that the page does not show.

Choose the Correct Article Type

For a standard company blog, BlogPosting is usually a sensible specific type. Article is also valid for general editorial content. NewsArticle should not be used merely because a post has a date; it is intended for news content and has its own expectations.

Use one primary article object per post. Add other types only when they describe a real, visible entity or feature. For example, an Organization publisher or Person author can be nested or referenced. Avoid adding Product, Review, LocalBusiness, or FAQPage simply to chase rich results.

Define a Validated Post Model

Do not build JSON-LD directly from unvalidated strings. Define the fields your blog parser guarantees:

type BlogPost = {
  title: string
  slug: string
  date: string
  lastUpdated?: string
  metaDescription: string
  author: string
  coverImage: string
}

Validation should reject or report:

  • missing title, slug, date, description, author, or cover image;
  • a frontmatter slug that differs from the filename or route;
  • invalid date formats;
  • a cover path that does not exist;
  • duplicate slugs;
  • an unknown author that cannot map to a visible profile; and
  • unsafe or malformed values.

If a required value is missing, it is better to fail the content check than publish invented schema defaults.

Create One Canonical URL Helper

The site origin should come from one trusted constant or configuration. Normalize extra slashes and never derive the public canonical from an untrusted request host.

const SITE_URL = "https://www.example.com"

function blogUrl(slug: string) {
  return `${SITE_URL}/blog/${encodeURIComponent(slug)}`
}

function absoluteUrl(pathOrUrl: string) {
  return pathOrUrl.startsWith("http")
    ? pathOrUrl
    : new URL(pathOrUrl, SITE_URL).toString()
}

The same helper should feed:

  • alternates.canonical;
  • openGraph.url;
  • JSON-LD url;
  • JSON-LD mainEntityOfPage.@id; and
  • sitemap output.

Next.js documents metadataBase as a way to resolve relative metadata URLs. Its current generateMetadata reference also notes that nested metadata objects such as openGraph are shallowly replaced by later route segments. When a blog route sets Open Graph data, include every field that page needs rather than assuming all nested parent fields will merge.

Build the BlogPosting Object

Keep the builder pure so it can be tested without rendering a React page:

function buildBlogPosting(post: BlogPost) {
  const url = blogUrl(post.slug)

  return {
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    headline: post.title,
    description: post.metaDescription,
    image: [absoluteUrl(post.coverImage)],
    datePublished: post.date,
    dateModified: post.lastUpdated || post.date,
    mainEntityOfPage: {
      "@type": "WebPage",
      "@id": url,
    },
    url,
    author: {
      "@type": "Person",
      name: "Verified Author Name",
      url: `${SITE_URL}/authors/verified-author`,
    },
    publisher: {
      "@type": "Organization",
      name: "Example Company",
      url: SITE_URL,
      logo: {
        "@type": "ImageObject",
        url: `${SITE_URL}/logo.png`,
      },
    },
  }
}

Google's current Article structured data guide recommends connecting visible authors to useful author URLs and using the correct Person or Organization type. Do not put text such as "Written by" inside the author name. The visible byline and JSON-LD author should match.

Render JSON-LD Safely in an App Router Page

The current Next.js JSON-LD guide recommends rendering a script in a page or layout. It also warns that plain JSON.stringify does not sanitize malicious strings. At minimum, replace < with its Unicode escape so content cannot close the script element.

function serializeJsonLd(value: unknown) {
  return JSON.stringify(value).replace(/</g, "\\u003c")
}

export default async function BlogPage({ params }: BlogPageProps) {
  const { slug } = await params
  const post = getPostBySlug(slug)
  const jsonLd = buildBlogPosting(post)

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: serializeJsonLd(jsonLd) }}
      />
      <article>{/* Render the visible MDX article here. */}</article>
    </>
  )
}

If frontmatter or MDX content can be edited by untrusted users, review a stronger serialization approach and your application's complete content-security model. Do not assume a static-looking content field is safe forever.

Keep Metadata and Schema in Sync

Generate metadata from the same post record:

export async function generateMetadata({ params }: BlogPageProps) {
  const { slug } = await params
  const post = getPostBySlug(slug)
  const url = blogUrl(post.slug)
  const image = absoluteUrl(post.coverImage)

  return {
    title: post.title,
    description: post.metaDescription,
    alternates: { canonical: url },
    openGraph: {
      type: "article",
      url,
      title: post.title,
      description: post.metaDescription,
      images: [{ url: image, alt: post.title }],
      publishedTime: post.date,
      modifiedTime: post.lastUpdated || post.date,
    },
  }
}

Do not create a second title or description specifically for schema unless there is a documented reason. Multiple independently maintained values drift over time.

Dates: Published Versus Modified

datePublished should represent the original publication date. dateModified should change when the page receives a meaningful content update, not when a build runs or whitespace changes.

A safe content workflow preserves the original date, updates lastUpdated after substantive editing, displays both dates where useful, updates structured data, and supplies an accurate sitemap modification date. Fake freshness can damage trust and makes audits unreliable.

Use an ISO-compatible date value and one timezone policy across content, metadata, schema, and sitemap generation.

Images and Publisher Data

Article images should be relevant, crawlable, indexable, and available at absolute URLs in structured data. Validate that each cover exists in the static output and has useful visible alt text. Do not point schema at a missing social image or a private storage URL.

Publisher data should use the real brand name, final canonical URL, and a stable logo. If the logo changes, update the source helper rather than editing hundreds of posts.

FAQ Schema Is Not a Default Blog Requirement

Visible FAQs can improve usability, but do not automatically add FAQPage schema to every article. Google limits FAQ rich-result eligibility and may change supported appearances. Even when markup is technically allowed, every question and answer must be visible and accurately represented.

For most business blogs, a clean BlogPosting plus visible question-and-answer sections is sufficient. Use the markup type that matches the page's primary purpose.

Common Architecture Mistakes

Injecting schema from each MDX file

Allowing every author to paste raw JSON-LD creates duplicate scripts, invalid syntax, inconsistent domains, and security risk. Generate schema centrally from structured frontmatter.

Using the current browser URL

Client-side window.location makes schema dependent on hydration and can preserve query strings or the wrong domain. Build the canonical on the server from trusted route data.

Publishing multiple conflicting article objects

A layout plugin, page component, and MDX body may each add Article schema. Inspect the final HTML and keep one authoritative object unless multiple articles are genuinely present.

Marking invisible or invented data

Do not add ratings, reviews, prices, FAQs, credentials, or organizations that the user cannot verify in the visible page. Google's guidelines require markup to represent the page.

Forgetting static export validation

A successful development page does not prove the exported HTML is correct. Build the site and inspect the generated artifact for every selected sample.

Next.js MDX metadata and schema data flow

Build-Time Validation Checklist

For every post, validate:

  • [ ] frontmatter parses successfully;
  • [ ] slug is unique and matches the public route;
  • [ ] title, description, author, date, and cover are present;
  • [ ] cover and inline image files exist;
  • [ ] canonical uses the final HTTPS domain;
  • [ ] Open Graph URL equals canonical;
  • [ ] JSON-LD URL and mainEntityOfPage equal canonical;
  • [ ] author maps to a visible author or About page;
  • [ ] dateModified is not earlier than datePublished;
  • [ ] JSON-LD parses as valid JSON;
  • [ ] only one primary BlogPosting object is present; and
  • [ ] no noindex appears on an indexable post.

A small validation script can parse generated HTML, extract each JSON-LD script, run JSON.parse, and compare URLs. This catches errors that source-level tests miss.

Deployment Validation

  1. Run the production build.
  2. Open representative generated HTML files.
  3. Verify canonical, Open Graph, and JSON-LD URLs.
  4. Test pages with Google's Rich Results Test.
  5. Inspect the deployed final URL in Search Console.
  6. Monitor enhancement reports and deployment regressions.

Google says structured data can enable a search feature but does not guarantee display. Measure clean validation and accurate entity understanding, not only whether a rich result appears.

How VASUYASHII Applies This Pattern

Current VASUYASHII blog implementation derives canonical, Open Graph, and article schema data from parsed content and the final https://www.vasuyashii.com domain. Content refreshes preserve public slugs and original dates, update modification dates only after meaningful changes, and validate generated output after a production build.

Related implementation guides include duplicate-without-canonical fixes in Next.js, duplicate canonical issues, automatic sitemap updates, SEO-friendly URL slugs, and technical website audits.

For implementation support, review web application development, custom software services, and integration services.

Choose the Type Before Coding It

Use the schema types for service business websites guide to decide when Organization, LocalBusiness, Service, BreadcrumbList, BlogPosting and FAQPage are accurate, Google-supported, visible, and worth maintaining.

FAQs

Should schema be stored inside MDX frontmatter?

Store clean source fields such as title, dates, author, description, and image in frontmatter. Generate the JSON-LD centrally. Raw schema in each file is harder to validate and maintain.

Is BlogPosting better than Article?

Both can describe editorial content. BlogPosting is a more specific type for a blog post. Choose the type that accurately represents the page and follow Google's Article guidance.

Can JSON-LD be rendered in a Client Component?

It can appear after client rendering, but a server-rendered page or layout is usually simpler and more reliable for static or server output. Keep content and metadata generation near the route's trusted data source.

Why replace &lt; during JSON serialization?

Untrusted text containing HTML-like content could otherwise interfere with the script element. Next.js explicitly warns about this risk in its JSON-LD guide. Review stronger sanitization when content is not fully trusted.

Should every FAQ section have FAQ schema?

No. Add only markup that accurately matches visible content and a supported use case. FAQ sections can still help readers without FAQ structured data.

How do I detect duplicate schema?

Inspect the final generated HTML, count application/ld+json scripts, parse their @type values, and identify multiple Article or BlogPosting objects describing the same page.

Does valid schema improve ranking?

Structured data can help search engines understand content and can enable supported search features. It is not a direct guarantee of ranking improvement or rich-result display.

Next Step

Audit one generated blog page from source to deployment. Compare the visible byline, dates, image, canonical, Open Graph URL, and JSON-LD values. Once that pattern is correct, automate the same checks for the full content library.