76

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

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Ivan M
  • 867
  • 1
  • 6
  • 8
  • Duplicated of https://stackoverflow.com/questions/46443652/react-16-warning-expected-server-html-to-contain-a-matching-div-in-body. 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. – AxeEffect Dec 20 '17 at 13:12

7 Answers7

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')
);
小弟调调
  • 1,315
  • 1
  • 17
  • 33
  • 1
    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. – html_programmer May 28 '19 at 22:13
  • 2
    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 ? – gaurav5430 Aug 05 '21 at 13:56
  • 17
    what about in nextjs, coz there is no root id in public folder – Mirza Irtiza Jan 30 '22 at 18:32
  • 1
    What does this do? – samuelnehool Feb 08 '23 at 20:02
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

aquinq
  • 1,318
  • 7
  • 18
  • 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. – Mossen Apr 05 '21 at 23:25
  • 4
    this just solves the development mode warning, this would not avoid the markup difference if you are server side rendering in production. – gaurav5430 Aug 05 '21 at 13:58
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"))
sunknudsen
  • 6,356
  • 3
  • 39
  • 76
8

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.

Ludovic
  • 223
  • 3
  • 9
2

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

Jannis Hell
  • 736
  • 8
  • 17
1

HTTP Client Hints could help you with this.

Another interesting article regarding Client Hints.

Martin Ždila
  • 2,998
  • 3
  • 31
  • 35
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.

Ben P.P. Tung
  • 1,224
  • 13
  • 21