PortableText rendering issue only in production build, not in dev

11 replies
Last updated: Feb 14, 2022
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
All links look correct in the data both prod and dev but rendering differently.
Note the attached images showing the hover with wrong link in prod vs dev (correct).

{
  "_id": null,
  "__typename": "SanitySectionCard",
  "_key": "760651869c6f",
  "narrowWidth": null,
  "_rawBody": [
    {
      "_key": "67630a4a912f",
      "_type": "block",
      "children": [
        {
          "_key": "02ac9a0b9592",
          "_type": "span",
          "marks": [
            "strong",
            "textCenter"
          ],
          "text": "ONLINE RESOURCES"
        }
      ],
      "markDefs": [],
      "style": "normal"
    },
    {
      "_key": "0cf9832a1c52",
      "_type": "block",
      "children": [
        {
          "_key": "da53108a6d4b0",
          "_type": "span",
          "marks": [
            "276420a6692f",
            "textCenter"
          ],
          "text": "Print Your ID at Home"
        }
      ],
      "markDefs": [
        {
          "_key": "276420a6692f",
          "_type": "link",
          "href": "<http://embed.academyart.edu/embed?e=f8e8fc84-1f52-48e3-8136-f696c15c4b90>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "a3ce50e0910c",
      "_type": "block",
      "children": [
        {
          "_key": "cdae97c580ee0",
          "_type": "span",
          "marks": [
            "2acd61c23e9b",
            "textCenter"
          ],
          "text": "Career Development"
        }
      ],
      "markDefs": [
        {
          "_key": "2acd61c23e9b",
          "_type": "link",
          "href": "<https://my.academyart.edu/resources/career-services>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "570e44a1d6eb",
      "_type": "block",
      "children": [
        {
          "_key": "53174ef4af4b0",
          "_type": "span",
          "marks": [
            "277857c3de63",
            "textCenter"
          ],
          "text": "Help Desk"
        }
      ],
      "markDefs": [
        {
          "_key": "277857c3de63",
          "_type": "link",
          "href": "<https://admin.academyart.edu/admin/report.do>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "5030ef1cab69",
      "_type": "block",
      "children": [
        {
          "_key": "02eefcf41fdc0",
          "_type": "span",
          "marks": [
            "d64bf603704f",
            "textCenter"
          ],
          "text": "Scholarships"
        }
      ],
      "markDefs": [
        {
          "_key": "d64bf603704f",
          "_type": "link",
          "href": "<https://www.academyart.edu/finances/scholarships/>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "2bc677099081",
      "_type": "block",
      "children": [
        {
          "_key": "08229471c057",
          "_type": "span",
          "marks": [
            "textCenter",
            "fe8893c15e0c"
          ],
          "text": "Guest Speaker Archive"
        }
      ],
      "markDefs": [
        {
          "_key": "fe8893c15e0c",
          "_type": "link",
          "href": "<https://speakers.academyart.edu/content/guest-speakers.html>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "47d9f3073c28",
      "_type": "block",
      "children": [
        {
          "_key": "1dfd00ed5495",
          "_type": "span",
          "marks": [
            "textCenter",
            "7bc72f30f1be"
          ],
          "text": "Spring Show Gallery"
        }
      ],
      "markDefs": [
        {
          "_key": "7bc72f30f1be",
          "_type": "link",
          "href": "<https://springshow.academyart.edu>",
          "style": "link"
        }
      ],
      "style": "normal"
    }
  ],
  "_rawBodyRight": [
    {
      "_key": "e15db2ed4f45",
      "_type": "block",
      "children": [
        {
          "_key": "b72ce86e0653",
          "_type": "span",
          "marks": [
            "strong",
            "textCenter"
          ],
          "text": "ACADEMIC RESOURCES"
        }
      ],
      "markDefs": [],
      "style": "normal"
    },
    {
      "_key": "ecd71729fdd4",
      "_type": "block",
      "children": [
        {
          "_key": "5e5f209a511d0",
          "_type": "span",
          "marks": [
            "c0c49961c516",
            "textCenter"
          ],
          "text": "Homework Help"
        }
      ],
      "markDefs": [
        {
          "_key": "c0c49961c516",
          "_type": "link",
          "href": "<https://aa-myacademy-web.vercel.app/my-academy/academy-resource-center/online-support/>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "461184bf2c6e",
      "_type": "block",
      "children": [
        {
          "_key": "f6763a95c2510",
          "_type": "span",
          "marks": [
            "9da6b4508912",
            "textCenter"
          ],
          "text": "Library"
        }
      ],
      "markDefs": [
        {
          "_key": "9da6b4508912",
          "_type": "link",
          "href": "<https://library.academyart.edu/>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "9013ab68f551",
      "_type": "block",
      "children": [
        {
          "_key": "261130a277230",
          "_type": "span",
          "marks": [
            "9748da10624d",
            "textCenter"
          ],
          "text": "Music Library"
        }
      ],
      "markDefs": [
        {
          "_key": "9748da10624d",
          "_type": "link",
          "href": "<http://discussion.academyart.edu/music>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "7e31696fe4b4",
      "_type": "block",
      "children": [
        {
          "_key": "f4f83e3c975e0",
          "_type": "span",
          "marks": [
            "41755f4fb89b",
            "textCenter"
          ],
          "text": "Software Tutorials"
        }
      ],
      "markDefs": [
        {
          "_key": "41755f4fb89b",
          "_type": "link",
          "href": "<http://resources.academyart.edu/student-resources/aau-101-resources/total-training.html>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "283d2e25fc15",
      "_type": "block",
      "children": [
        {
          "_key": "65e9936fafe70",
          "_type": "span",
          "marks": [
            "506c9446cb0b",
            "textCenter"
          ],
          "text": "Textbooks"
        }
      ],
      "markDefs": [
        {
          "_key": "506c9446cb0b",
          "_type": "link",
          "href": "<http://aoa.ecampus.com/>"
        }
      ],
      "style": "normal"
    },
    {
      "_key": "9418812e4687",
      "_type": "block",
      "children": [
        {
          "_key": "53f51481f30f0",
          "_type": "span",
          "marks": [
            "a4156fd9c447",
            "textCenter"
          ],
          "text": "Class Supplies"
        }
      ],
      "markDefs": [
        {
          "_key": "a4156fd9c447",
          "_type": "link",
          "href": "<http://howto.academyart.edu/manage/lms/buying-class-supplies>"
        }
      ],
      "style": "normal"
    }
  ],
  "backgroundColor": {
    "_key": null,
    "_type": null,
    "title": "White",
    "value": "white"
  }
}
div
in
a
is another issue will fix that 🙂
Data for link highlighted:
{
      "_key": "47d9f3073c28",
      "_type": "block",
      "children": [
        {
          "_key": "1dfd00ed5495",
          "_type": "span",
          "marks": [
            "textCenter",
            "7bc72f30f1be"
          ],
          "text": "Spring Show Gallery"
        }
      ],
      "markDefs": [
        {
          "_key": "7bc72f30f1be",
          "_type": "link",
          "href": "<https://springshow.academyart.edu>",
          "style": "link"
        }
      ],
      "style": "normal"
    }
is somehow picking up this other links url in production build even tho same data:

{
      "_key": "570e44a1d6eb",
      "_type": "block",
      "children": [
        {
          "_key": "53174ef4af4b0",
          "_type": "span",
          "marks": [
            "277857c3de63",
            "textCenter"
          ],
          "text": "Help Desk"
        }
      ],
      "markDefs": [
        {
          "_key": "277857c3de63",
          "_type": "link",
          "href": "<https://admin.academyart.edu/admin/report.do>"
        }
      ],
      "style": "normal"
    },
Data for link highlighted:
{
      "_key": "47d9f3073c28",
      "_type": "block",
      "children": [
        {
          "_key": "1dfd00ed5495",
          "_type": "span",
          "marks": [
            "textCenter",
            "7bc72f30f1be"
          ],
          "text": "Spring Show Gallery"
        }
      ],
      "markDefs": [
        {
          "_key": "7bc72f30f1be",
          "_type": "link",
          "href": "<https://springshow.academyart.edu>",
          "style": "link"
        }
      ],
      "style": "normal"
    }
is somehow picking up this other links url in production build even tho same data:

{
      "_key": "570e44a1d6eb",
      "_type": "block",
      "children": [
        {
          "_key": "53174ef4af4b0",
          "_type": "span",
          "marks": [
            "277857c3de63",
            "textCenter"
          ],
          "text": "Help Desk"
        }
      ],
      "markDefs": [
        {
          "_key": "277857c3de63",
          "_type": "link",
          "href": "<https://admin.academyart.edu/admin/report.do>"
        }
      ],
      "style": "normal"
    },
Tried changing the link mark in our code to the default example same issue so pretty sure it's not our serializer code.
Any good place to start debugging this in the react portable text lib?
Does react portable text have any logic switching on dev vs prod or is this a gatsby issue? I even tried downgrading gatsby and other libs no luck.
Hmm I removed my serializer link mark completely and it fixed it so I guess it is related to our serializer code. Could it be something with this? I'll try
useMemo
https://github.com/portabletext/react-portabletext#using-a-react-context
Hmm I removed my serializer link mark completely and it fixed it so I guess it is related to our serializer code. Could it be something with this? I'll try
useMemo
https://github.com/portabletext/react-portabletext#using-a-react-context
Well this sounds crazy but fixing the nested
div
inside the
a
fixed it! I'll update the gh issue.. might be some side effect with react dom rendering cacheing something if invalid dom nesting 🤷 hopefully this helps someone else later.
Thanks for walking us through how you found a solution!

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?