PortableText rendering issue only in production build, not in dev

11 replies
Last updated: Nov 29, 2025
Hi, has anyone experienced this before? I'm stumped on this one I tried upgrading to the new react/portabletext library and the same issue exists. The really strange thing is it only shows in production build not in dev. https://github.com/sanity-io/block-content-to-react/issues/59
AI Update

I've seen this issue before! The good news is there's a known solution for this specific problem with Portable Text links showing wrong URLs in Gatsby production builds.

This is actually a React key issue that only manifests in production builds due to how React optimizes rendering. The root cause is typically invalid HTML nesting in your custom serializers/components.

As the original poster discovered in the GitHub issue, the problem was having a <div> nested inside an <a> tag in their custom marks serializer. Block-level elements like div inside inline elements like a creates invalid HTML, which React handles differently in development vs production mode.

The Solution

Change any block-level elements in your link marks to inline elements:

Bad:

marks: {
  link: ({value, children}) => (
    <a href={value.href}>
      <div>{children}</div>  // ❌ Block element inside inline
    </a>
  )
}

Good:

marks: {
  link: ({value, children}) => (
    <a href={value.href}>
      <span>{children}</span>  // ✅ Inline element
    </a>
  )
}

Or just use the children directly:

marks: {
  link: ({value, children}) => (
    <a href={value.href}>{children}</a>
  )
}

Why This Happens

  • In development, React is more forgiving with invalid HTML nesting
  • In production builds, React's optimizations cause it to reuse DOM nodes incorrectly when the HTML structure is invalid
  • This leads to href attributes getting "stuck" on the wrong links
  • Gatsby's SSR/hydration process amplifies this issue

Additional Checks

If you've already migrated to @portabletext/react (the modern replacement for the deprecated block-content-to-react), make sure you're also:

  1. Providing proper key props if you're mapping over custom components
  2. Not wrapping marks in block-level elements
  3. Following proper HTML semantics in all your custom serializers

This production-only bug with reused URLs is almost always a sign of invalid HTML nesting in your Portable Text components!

Show original thread
11 replies

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?