React SSR: Document is not defined

12,164

Solution 1

This issue usually happens because when react is rendered on the server. It does not have a document or window object on the server side and those objects are only available on the browser.

Try to call the document functions in or after componentDidMount.

componentDidMount(){
  this.setState({documentLoaded:true});
}

someFunction(){
  const { documentLoaded } = this.state;
  if(documentLoaded){
     // LOGIC USING DOCUMENT OBJECT
  }
}

Solution 2

If you are using react-hooks, you can create your custom useDocument hook:

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

export const useDocument = () => {
  const [myDocument, setMyDocument] = useState(null)
   
  useEffect(() => {
    setMyDocument(document)
  }, [])

  return myDocument
}

in your component:

...
const doc = useDocument()
...

<SomeComponent
 ref={doc && doc.body}
 ...
/>
...
Share:
12,164
ZpfSysn
Author by

ZpfSysn

Updated on June 19, 2022

Comments

  • ZpfSysn
    ZpfSysn almost 2 years

    I have been working on this for two days now. Looked through multiple stack posts and still not found a suitable answer.

    I am trying to rendering my react project in server like following:

    Server.js

    function handleRender(req,res){
    
      const sheetsRegistry = new SheetsRegistry();
    
      const sheetsManager = new Map();
    
      const theme = createMuiTheme({
        palette:{
          primary:green,
          accent: red,
          type: 'light',
        }
      })
    
      const generateClassName = createGenerateClassName();
    
      const html = ReactDOMServer.renderToString(
        <JssProvider registry={sheetsRegistry} generateClassName={generateClassName}>
          <MuiThemeProvider theme={theme} sheetsManager={sheetsManager}>
            <TwoFA />
          </MuiThemeProvider>
        </JssProvider>
      )
    
      const css = sheetsRegistry.toString()
    
      res.send(renderFullPage(html,css))
    }
    
    function renderFullPage(html,css){
      return   `
        <!DOCTYPE html>
        <html>
          <head>
            <title>2FA SDK</title>
          </head>
          <body style="margin:0">
            <div id="app">${html}</div>
            <script id="jss-server-side">${css}</script>
          </body>
        </html>
      `
    }
    

    Client.js:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import TwoFA from './App';
    import {
        MuiThemeProvider,
        createMuiTheme,
        createGenerateClassName,
      } from '@material-ui/core/styles';
    import green from '@material-ui/core/colors/green';
    import red from '@material-ui/core/colors/red';
    
    class Main extends React.Component{
        componentDidMount() {
            const jssStyles = document.getElementById('jss-server-side');
            if (jssStyles && jssStyles.parentNode) {
              jssStyles.parentNode.removeChild(jssStyles);
            }
        }
    
        render(){
            return <TwoFA />
        }
    }
    
    const theme = createMuiTheme({
        palette: {
          primary: green,
          accent: red,
          type: 'light',
        },
      });
    
    const generateClassName = createGenerateClassName();
    
    if (typeof window !== 'undefined'){
        ReactDOM.hydrate(
            <JssProvider generateClassName={generateClassName}>
              <MuiThemeProvider theme={theme}>
                  <TwoFA/>
              </MuiThemeProvider>
            </JssProvider>,
            document.querySelector('#app'),
        );
    }
    

    Webpack.config.js

    module.exports = [
        {
            /*Config for backend code*/ 
            entry: './src/server/server.js',
            output: {
                filename: 'server.js'
            },
            externals: [nodeExternals()],
            module: {
                rules: [
                    {
                        test: /\.(js|jsx)$/,
                        exclude: /node_modules/,
                        use: {
                            loader: "babel-loader"
                        }
                    },
                    {
                        test: /\.html$/,
                        use: {
                            loader: "html-loader",
                            options: { minimize: true }
                        }
                    },
                    {
                        test: /\.css$/,
                        use: [MiniCssExtractPlugin.loader,"css-loader"]
                    }
                ]
            },
            plugins: [
                new HtmlWebPackPlugin({
                    template: "./public/index.html",
                    filename:"./index.html"
                }),
                new MiniCssExtractPlugin({
                    filename: "[name].css",
                    chunkFilename:"[id].css"
                })
            ]
        },
        { 
            entry: './src/client.js',
            output: {
               filename: 'bundle.js',
            },
            module: {
               rules: [
                    {
                        test: /\.js$/,
                        exclude: /node_modules/,
                        loader: 'babel-loader',
                    },
                    {
                        test: /\.html$/,
                        use: {
                            loader: "html-loader",
                            options: { minimize: true }
                        }
                    },
                    {
                        test: /\.css$/,
                        use: [MiniCssExtractPlugin.loader,"css-loader"]
                    }
                ],
            },
            plugins: [
                new HtmlWebPackPlugin({
                    template: "./public/index.html",
                    filename:"./index.html"
                }),
                new MiniCssExtractPlugin({
                    filename: "[name].css",
                    chunkFilename:"[id].css"
                })
            ]
        }
    ]
    

    What I have tried: I search on SO and found that many posts suggesting put a condition check like so : if (typeof window !== 'undefined'). However, this does not solve the problem.

    I also understood that the error is due to the fact that during SSR, server-side has no document project.

    I have searched on github issue page and someone mentioned that he ran into the problem with webpack, but same project worked fine with browserify.

    What I need help with: I am trying to solve this problem as it cause the app to break.

    I am suspecting that there is something wrong with webpack. I am looking a fix for this