Reputation: 997
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
Upvotes: 89
Views: 175584
Reputation: 1
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>
</>
)
}
Upvotes: 0
Reputation: 775
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
) insideuseEffect
, it will happen after hydration 👍
Upvotes: 4
Reputation: 1448
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
Upvotes: 22
Reputation: 263
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
.
Upvotes: 10
Reputation: 7310
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"))
Upvotes: 9
Reputation: 1333
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')
);
Upvotes: 37
Reputation: 1284
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.
Upvotes: 1
Reputation: 3219
HTTP Client Hints could help you with this.
Another interesting article regarding Client Hints.
Upvotes: 1