React 16: Warning: Expected server HTML to contain a matching <div> in <div> due to State

155.5k Views Asked by At

I'm getting the following error using SSR

Warning: Expected server HTML to contain a matching <div> in <div>.

The issue is on the client when checking the browser width on component mount, and then setting the state of a component to render a mobile version of it instead.

But the server is defaulting the desktop version of the container as it is not aware of the browser width.

How do I deal with such a case? Can I somehow detect the browser width on the server and render the mobile container before sending to the client?

EDIT: For now I've decided to render the container when the component mounts. This way, both server and client side render nothing initially preventing this error.

I'm still open to a better solution

8

There are 8 best solutions below

4
On BEST ANSWER

This will solve the issue.

// Fix: Expected server HTML to contain a matching <a> in
const renderMethod = module.hot ? ReactDOM.render : ReactDOM.hydrate;
renderMethod(
  <BrowserRouter>
    <RoutersController data={data} routes={routes} />
  </BrowserRouter>,
  document.getElementById('root')
);
0
On

In my case, I had to place the "Provider of Redux" and "Toaster" within the body tag in the Root layout.

export default function RootLayout({ children }) {
      return (
        <>
          <html lang="en">
            <body className={inter.className}>
            <Toaster position="top-right" reverseOrder={false} toastOptions={{ duration: 1000 }} />
              <StoreProvider>
                {children}
              </StoreProvider>
            </body>
          </html>
        </>
      )
    }
2
On

Gatsby

A recent feature flag of gatsby (introduced in v2.28, December 2020) ables to server-side render pages in dev environment.

This flag is set to true by default. In this case, you might see this error message in the console

Warning: Expected server HTML to contain a matching <div> in <div>.

You can disable this flag in gatsby.config.js file :

module.exports = {
  flags: {
    DEV_SSR: false,
  }
}

doc : https://www.gatsbyjs.com/docs/reference/release-notes/v2.28/#feature-flags-in-gatsby-configjs

0
On

From https://github.com/vercel/next.js/discussions/17443#discussioncomment-87097

Code that is only supposed to run in the browser should be executed inside useEffect. That's required because the first render should match the initial render of the server. If you manipulate that result it creates a mismatch and React won't be able to hydrate the page successfully.

When you run browser only code (like trying to access window) inside useEffect, it will happen after hydration

1
On

The current accepted answer doesn’t play well with TypeScript. Here is what works for me.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
  </body>
</html>
import React from "react"
import { hydrate, render } from "react-dom"
import BrowserRouter from "./routers/Browser"

const root = document.getElementById("root")
var renderMethod
if (root && root.innerHTML !== "") {
  renderMethod = hydrate
} else {
  renderMethod = render
}
renderMethod(<BrowserRouter />, document.getElementById("root"))
0
On

This message can also occurs due to bad code that doesn't render consistent content between your SSR and CSR, thus hydrate can't resolve.

For example SSR returns :

...
<div id="root">
   <div id="myDiv">My div content</div>
</div>
...

While CSR returns :

...
<div id="root">
    <div id="anotherDiv">My other div content</div>
</div>
...

The best solution in this case is not installing libraries or turning off hydrate but actually fix the inconsistency in your code.

Temporaryly removing <script src="/react-bundle-path.js"></script> from index.js can help to compare the exact content rendered by SSR with content rendered by CSR hydrate.

0
On

My solution is to use a middleware like express-useragent to detect the browser user agent.

Then, in the server side, create a viewsize like {width, height} by the following rules

if (ua.isMobile) {
  return {width: 360, height: 480}
}

if (ua.isDesktop) {
  return {width: 768, height: 600}
}

return {width: 360, height: 480} // default, and for bot

Then, it is still somehow a responsive design in SSR.

2
On