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

8

88

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

Sauceda answered 21/10, 2017 at 17:13 Comment(1)
Duplicated of #46444152. Be sure if you are using the React.hydrate() method is because you are using SSR. If not use React.render(). You can also use the suppressHydrationWarning={true} prop on the rendered element. But don't overuse it.Jackfish
S
36

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')
);
Stridulous answered 29/11, 2018 at 13:3 Comment(4)
It is. In the end you should have to restart the server. However in the end if you're using the same components for client vs server, it wouldn't make a difference. If you however want auto-reload the browser on server updates as well, you still need to auto-reload the server.Merth
how does this solve the problem? This seems that if hot module relaoding is enabled, just use the SPA method for client side rendering But in production builds, there would still be a problem of markup mismatch ?Disputable
what about in nextjs, coz there is no root id in public folderCornucopia
What does this do?Moise
P
22

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

Possessed answered 31/1, 2021 at 15:17 Comment(2)
from the document: DEV_SSR - Server-side render (SSR) pages on full reloads during develop. Helps you detect SSR bugs and fix them without needing to do full builds. This fix the warning but disable other features.Calzada
this just solves the development mode warning, this would not avoid the markup difference if you are server side rendering in production.Disputable
S
10

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.

Southeastward answered 27/3, 2021 at 21:14 Comment(0)
B
9

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"))
Bipod answered 29/3, 2020 at 9:36 Comment(1)
this solution worked for me, React/React-DOM ^16.13.0Dangerfield
S
4

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 👍

Sophister answered 4/12, 2022 at 14:17 Comment(0)
G
1

HTTP Client Hints could help you with this.

Another interesting article regarding Client Hints.

Gesellschaft answered 3/11, 2017 at 13:11 Comment(2)
The link you provided is no longer working, please paste relevant information here for future reference.Arrangement
@Arrangement thanks for notifying. I've updated the ling and added another one.Barchan
C
1

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.

Clare answered 3/5, 2018 at 5:28 Comment(0)
C
0

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>
        </>
      )
    }
Conciliatory answered 27/11, 2023 at 7:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.