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

Viewed 87266

Since upgrading to React 16 I get this error message:

warning.js:33 Warning: Expected server HTML to contain a matching <div> in <body>.

What is generally causing this error message and how can it be fixed?

10 Answers

If you use ReactDOM.hydrate to start web application, you will see this warning.

If your application does not use server-side rendering (ssr), please use ReactDOM.render to start.

If you're using Server Side Rendering like NextJS, delete recent code and compare if you've tried to access a variable directly inside of Component scope where DOM is not guaranteed yet. For me, it was:

import { i18n } from 'i18n'

export default function SomeComponent() {
    const initLanguage = i18n.language              <---- causing error

    return ...
}

If you need to access such properties, access it within useEffect, so as to make sure that document is already established by then. It is kinda equivalent to componentDidMount():

import { i18n } from 'i18n'
import { useEffect, useState } from 'react'

export default function SomeComponent() {
    const [initlanguage, setInitLanguage] = useState('en')

    useEffect(() => setInitLanguage(i18n.language), [])

    return ...
}

If your HTML code is like

<table>
  <tr>

you can get this error.
To get around it, use the <tbody> tag like

<table>
  <tbody>
    <tr>

Don't forget to close the tag(s)!

This seems to be because of Browsersync inserting a script tag in the body on client side that does not exist on server side. Thus React fails to attach to the server render.

I got this using material UI by trying to do const searchParams = new URLSearchParams(process.browser ? window.location.search : '') at the top of the react component in my NextJS app with material-ui SnackBar, I was able to remove the error by putting this in a useEffect hook.

Entire component for reference:

export default function SnackBarMessage() {
  const [requestLogin, setRequestLogin] = useState(false)
  const handleClose = (event, reason) => {
    if (reason === 'clickaway') {
      return
    }

    setRequestLogin(false)
  }

  useEffect(() => {
    // had to move into a useEffect hook
    const searchParams = new URLSearchParams(process.browser ? window.location.search : '')
    const hasRequestLogin = Boolean(searchParams.get('requestLogin'))
    if (hasRequestLogin) {
      setRequestLogin(true)
    }
  }, [])
  return (
    <>
      {requestLogin && (
        <Snackbar open={requestLogin} autoHideDuration={6000} onClose={handleClose}>
          <Alert onClose={handleClose} severity='error' style={{ fontSize: '18px' }} elevation={6}>
            Please Log Back In
          </Alert>
        </Snackbar>
      )}
    </>
  )
}

In my case it was because of using PersistGate and react-loadable. if you using this libraries, you could use preloadAll instead of preloadReady

In my case i installed REACTFUL and it do a render so different by default install:

src/renderers/dom.js

ReactDOM.hydrate(
  <App initialData={window.__R_DATA.initialData} />,
  document.getElementById('root'),
);

src/renderers/server.js

const initialData = {
   appName: 'Reactful',
 };
return Promise.resolve({
   initialData,
   initialMarkup: ReactDOMServer.renderToString(
     <App initialData={initialData} />,
   ),
   pageData,
 });

care with initialData!

i fix it changed dom.js like it, attention to initialData is important:

const renderMethod = module.hot ? ReactDOM.render : ReactDOM.hydrate;
const initialData = {
  appName: 'Reactful',
};
renderMethod(
  <App initialData={initialData} />,
  document.getElementById('root'),
);

The issue can be caused because the client and server mismatch during HMR updates

I resolved that way:

const renderMethod = !!module.hot ? ReactDOM.render : ReactDOM.hydrate

const renderMethod = !!module.hot ? ReactDOM.render : ReactDOM.hydrate
renderMethod(
  <AppContainer>
    <Comp />
  </AppContainer>,
  document.getElementById('root')
)

Looks like you're trying to access a prop in a dom before is ready. You can use a structure like this:

{(variable) && sameVariable}

I'm assuming you are using ssr. The warning is about attempting to render before there is a window object. You need to hydrate.

ReactDOM.hydrate(<App />, document.getElementById("home"))

What I don't understand, is that the App component is being served static by express. Why is it trying to render before it is served? Bring on the comments.

Related