facebook / react

The library for web and native user interfaces.
https://react.dev
MIT License
227.13k stars 46.31k forks source link

Bug: Hydration fails when async script is located outside head tag #29801

Closed atstoyanov closed 2 months ago

atstoyanov commented 3 months ago

I have a nextjs application that have to render a json-ld schema. The easiest place to generate the schema was in a page component responsible for loading product. I though using the special rendering behaviour of scripts will move the<script> tag from the <body> into the <head> section. I was testing with postman because I wanted to see only the initial version of the page and be sure that the script element is in the <head> section. Which wasn't. The problem was a client component that was rendering a loading screen and preventing everything underneath to be rendered on the server. I thought this is safe for now and tried to run the code in the browser which led to this error:

Update: The issue exist also when navigating to a page that has

...
  <script
        async
        src=' '
        type='application/ld+json'
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
...

using nextjs Link or router.push().

Screenshot 2024-06-07 at 19 09 17

It seems react is trying to create a missing dom element during hydration (I might be wrong) and in the process setInitialProperties is called with a wrong parameter - link instead of script

        instance = ownerDocument.createElement('script');
        markNodeAsHoistable(instance);
        setInitialProperties(instance, 'link', scriptProps);
        (ownerDocument.head: any).appendChild(instance);

React version: 19.0.0-beta-26f2496093-20240514

Steps To Reproduce

Scenario 1

  1. clone the repo
  2. npm i && npm run dev
  3. git checkout main
  4. navigate to http://localhost:3000 The loading screen is shown for 2 seconds. After that the page freeze and the error is shown in the console. The tab might snap in a bit because of it will run out of memery.

Scenario 2

  1. clone the repo
  2. npm i && npm run dev
  3. git checkout navigation-example
  4. navigate to http://localhost:3000
  5. click 'Test Page' link The page hangs right away with the same error in console

Link to code example: https://github.com/atstoyanov/react-dom-bug

The current behavior

The expected behavior

The page renders and the <script> component is moved into the <head> section.

atstoyanov commented 2 months ago

The issue is reproducible when navigating from a nextjs Link to a page that tries to inject script into the header. Updated

eps1lon commented 2 months ago

Next.js 14 is not compatible with React 19.

Can you test this with next@15.0.0-canary.27 and react@19.0.0-rc.0 instead?

optonpo commented 2 months ago

It makes sense that 19 might require Next15, although I'm pretty sure Next14 began throwing this error about a month ago:

if (parseInt(React.version) < 19) { throw new Error('Next.js requires react >= 19.0.0 to be installed.') }

eps1lon commented 2 months ago

Some Next 14 canaries do that but no stable release.

optonpo commented 2 months ago

Thanks for the response! Ah, yep. it's all coming back to me. Next recommended 14canary as a solution to the 14.2/18.3 fetchPriority issue, which then immediately thrust me into this 14/19 hydration head-scratcher.

Looks like dialing back to 18.2 is the safest solution. Will check back in next year. Thanks again!

eps1lon commented 2 months ago

Closing this in the meantime since the current repro uses versions that are not compatible. If it still repros with Next.js 15 and React 19, please open a new issue.