react: Bug: `hydrateRoot().render` causes error, even though the #root's HTML from the server coincides the component

From react 18.1 docs:

React expects that the rendered content is identical between the server and the client. It can patch up differences in text content, but you should treat mismatches as bugs and fix them. In development mode, React warns about mismatches during hydration.

But when render() function is called for the hydrated root, the error occures:

This root received an early update, before anything was able hydrate. Switched the entire root to client rendering.

Root contains the same HTML as should be rendered by React

React & React DOM version: 18.1.0

Steps To Reproduce

  1. Insert an expected html to the root element imitating the string from a server
  2. Render app on a client side
import { StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { renderToString } from "react-dom/server";
const App = () => <h1>Hello</h1>

const rootString = renderToString(<App />);

const rootElement = document.getElementById("root");
rootElement.innerHTML = rootString;
const root = hydrateRoot(rootElement, <App />);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

Link to code example:

https://codesandbox.io/s/goofy-volhard-of1bmi?file=/src/index.js

The current behavior

The error means that react renders app twise; this causes unnecessary server requests even if data is already received and inserted to HTML

The expected behavior

React should add only listeners and shouldn’t render it twise

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 15

Most upvoted comments

👋 I spent a couple of hours figuring out why my app was complaining about mismatches. Thanks to this issue I could determine it was due to new lines. In my case I’m sending this from the server:

const componentHTML = ReactDOM.renderToString(<Root />);

res.send(
  <!DOCTYPE html>
  <html>
    <body>
    <div id="main-view">
      ${componentHTML}
    </div>
    </body>
  </html>
);

In the client I’m calling hydrateRoot:

ReactDOM.hydrateRoot(document.getElementById('main-view'), <Root />)

And this was the warning in the dev console: image

It turned out to be the new lines, I changed this:

<div id="main-view">
  ${componentHTML}
</div>

to this:

<div id="main-view">${componentHTML}</div>

It would be really nice to have a small text in the documentation about this. I know it’s a silly error but as there isn’t too much feedback it will save people time if there were a warning in the docs 😃

You only needed to change

        <div id="root">
          ${renderToString(<MemoryRouter initialEntries={[request.url]}><App /></MemoryRouter>)}
        </div>

to

        <div id="root">${renderToString(<MemoryRouter initialEntries={[request.url]}><App /></MemoryRouter>)}</div>

It’s specifically the difference within root that causes the issue.

Why this issue was closed? Turning new lines into one liner is not a fix… If you have multiple properties inside one component this is a no go. We have similar issue and yes for one component it can actually resolve it, but we can’t do it for all.