Gatsby: Deploying to netlify : error "document" is not available during server side rendering

Created on 13 Nov 2019  ·  6Comments  ·  Source: gatsbyjs/gatsby

Hi,

I was wondering if anyone has had this issue before or knows a way to fix the following issue when deploying to netlify using Gatsby.

Screen Shot 2019-11-13 at 12 23 11 AM

my code:
Portal.js
`
import { Component } from 'react'
import ReactDOM from 'react-dom'

// const isBrowser = typeof document !== 'undefined';
const portalRoot = document.getElementById('portal')
// const portalRoot = isBrowser ? document.getElementById('portal') : null;

export default class Portal extends Component {
constructor() {
super();
this.el = document.createElement('div')
// this.el = isBrowser ? document.createElement('div') : null;
}

componentDidMount = () => {
    portalRoot.appendChild(this.el)
}

componentWillUnmount = () => {
    portalRoot.removeChild(this.el)
}

render() {
    const { children } = this.props;
    return ReactDOM.createPortal(children, this.el)
}

}

`

I have try adding typeof document !== 'undefined' in componentDidMount but no success. anyone knows a way around it?

Most helpful comment

@vandesh not quite, my read on this and based on my knowledge what is happening with your code is that the return will "bail out " of the useeffect. As a test that you could do is just place a console.log() before the last return.
To demonstrate what i'm saying i've created a simple reproduction based on the information at hand. The code used below should not be used in any way/shape or form in a live site.

And here are the steps i took for this:

  • Created a Gatsby site based on the hello world starter.
  • Created a component called Wrapper inside src\components\ folder with the following code:
import React from "react"

const Wrapper = ({ children }) => (
  <div style={{ margin: "5px", backgroundColor: "rebeccapurple" }}>
    {children}
  </div>
)
export default Wrapper
  • Created a component called Popover inside the same folder as above with the following code:
import React from "react"

const Popover = ({ appendTo, placement }) => {
  const randomElement = document.createElement("h1")
  randomElement.innerHTML = `this is a random item injected via append`
  return (
    <div>
      <h1>look here</h1>
      <div dangerouslySetInnerHTML={{__html:appendTo.appendChild(randomElement)}}/>
    </div>
  )
}
export default Popover

This is just a pure extrapolation as i don't know what the content of your Popover component is and how it's built and if it's relevant to any package.

  • Created another component called MyComponent with the following code:
import React, { useEffect } from "react"
import Wrapper from "./Wrapper"
import Popover from "./Popover"
const MyComponent = () => {
  useEffect(() => {
    if (typeof window === "undefined" || !window.document) {
      console.log(`bailing out of the useeffect. Going to continue to render??`)
      return
    }
  }, [])

  console.log(`reached before render`)
  return (
    <Wrapper>
        <div>first iteration</div>
    </Wrapper>
  )
}
export default MyComponent
  • Changed src\pages\index.js to the following:
import React from "react"
import MyComponent from '../components/MyComponent'
export default () => <div><MyComponent/></div>

  • As is, issued yarn build && yarn serve to generate a production build it went through and opening up http://localhost:9000 i'm presented with the following:
    vandesh_1

If you see on the developer tools on the right side, technically the console.log(reached before render), shouldn't be posted, but it did.

  • I changed the code on MyComponent.js to the following:
import React, { useEffect } from "react"
import Wrapper from "./Wrapper"
import Popover from "./Popover"
const MyComponent = () => {
  useEffect(() => {
    if (typeof window === "undefined" || !window.document) {
      console.log(`bailing out of the useeffect. Going to continue to render??`)
      return
    }
  }, [])

  console.log(`reached before render`)
  return (
    <Wrapper>
      <Popover
              placement="bottom-end"
              appendTo={document.body} /> 
    </Wrapper>
  )
}
export default MyComponent
```` 

- Issued again a production build and i'm presented with the following:
```bash
$ gatsby build
success open and validate gatsby-configs - 0.022s
success load plugins - 0.078s
success onPreInit - 0.006s
success delete html and css files from previous builds - 0.022s
success initialize cache - 0.011s
success copy gatsby files - 0.240s
success onPreBootstrap - 0.016s
success createSchemaCustomization - 0.004s
success source and transform nodes - 0.027s
success building schema - 0.216s
success createPages - 0.003s
success createPagesStatefully - 0.067s
success onPreExtractQueries - 0.002s
success update schema - 0.094s
success extract queries from components - 0.108s
success write out requires - 0.010s
success write out redirect data - 0.005s
success onPostBootstrap - 0.007s
⠀
info bootstrap finished - 6.134 s
⠀
success Building production JavaScript and CSS bundles - 4.820s
success Rewriting compilation hashes - 0.007s
success run queries - 5.003s - 1/1 0.20/s
[                            ]   0.001 s 0/1 0% Building static HTML for pages
failed Building static HTML for pages - 0.521s

 ERROR #95312

"document" is not available during server side rendering.

See our docs page for more info on this error: https://gatsby.dev/debug-html


  15 |       <Popover
  16 |               placement="bottom-end"
> 17 |               appendTo={document.body} />
     |                         ^
  18 |     </Wrapper>
  19 |     // <Wrapper>
  20 |


  WebpackError: ReferenceError: document is not defined

  - MyComponent.js:17 MyComponent
    src/components/MyComponent.js:17:25


error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

As you can see by the output, the issue manifests itself, reason why it's happening is that when Gatsby is trying to generate the build he reaches a point that it doesn't know what document and starts throwing the error.

  • Changing the MyComponent.js to the following:
import React, { useEffect } from "react"
import Wrapper from "./Wrapper"
import Popover from "./Popover"
const MyComponent = () => {
  useEffect(() => {
    if (typeof window === "undefined" || !window.document) {
      console.log(`bailing out of the useeffect. Going to continue to render??`)
      return
    }
  }, [])

  console.log(`reached before render`)
  return (

    <Wrapper>

        {typeof window!=='undefined'?<Popover
              placement="bottom-end"
              appendTo={document.body} />:<div>something to be displayed if the document is not present</div> }

    </Wrapper>
  )
}
export default MyComponent

And issuing back a production build for the third time, the build goes through and if i open http://localhost:9000 i'm presented with the following:
vandesh_2

Once again my take on this is that despite returning from the useEffect hook it doesn't mean you'll be "bailing out" from the entire functional component, it just means you're bailing out of a function that is inside the hook but the rest of the code flow will run through.

All 6 comments

Hey @Madgeniusblink,

ReactDOM.createPortal does not work in SSR. You'll need to do your if window check in the component above so it's only loaded when React does it's browser hydration.

For more information please checkout https://www.gatsbyjs.org/docs/using-client-side-only-packages/

We're marking this issue as answered and closing it for now but please feel free to reopen this and comment if you would like to continue this discussion. We hope we managed to help and thank you for using Gatsby! 💜

In my case it seems similar to the issue above. I have the following code -

import React, { useEffect } from 'react';

const MyComponent = () => {
  useEffect(() => {
    if (typeof window === 'undefined' || !window.document) {
      return;
    }
  }, []);

  return (
    <Wrapper>
         <Popover
              placement="bottom-end"
              appendTo={document.body} /> 
    </Wrapper>
  );
}

As you can see, I have added a check for the window object in the useEffect hook within the component, but it still throws -

"document" is not available during server side rendering.

Am I missing something obvious here?

@vandesh the logic applies, as Gatsby is a server side rendering framework, some apis, like window and in this casedocument are not present during the build process. You'll have to do some additional checks with this. Or based on the use case of the component you could probably go with this, basically you move the component in question to client-side.

Thanks for the link @jonniebigodes
I understand that they're not present during the build process, but isn't that what the check in the useEffect hook is for? To bypass loading the component till window object is available

@vandesh not quite, my read on this and based on my knowledge what is happening with your code is that the return will "bail out " of the useeffect. As a test that you could do is just place a console.log() before the last return.
To demonstrate what i'm saying i've created a simple reproduction based on the information at hand. The code used below should not be used in any way/shape or form in a live site.

And here are the steps i took for this:

  • Created a Gatsby site based on the hello world starter.
  • Created a component called Wrapper inside src\components\ folder with the following code:
import React from "react"

const Wrapper = ({ children }) => (
  <div style={{ margin: "5px", backgroundColor: "rebeccapurple" }}>
    {children}
  </div>
)
export default Wrapper
  • Created a component called Popover inside the same folder as above with the following code:
import React from "react"

const Popover = ({ appendTo, placement }) => {
  const randomElement = document.createElement("h1")
  randomElement.innerHTML = `this is a random item injected via append`
  return (
    <div>
      <h1>look here</h1>
      <div dangerouslySetInnerHTML={{__html:appendTo.appendChild(randomElement)}}/>
    </div>
  )
}
export default Popover

This is just a pure extrapolation as i don't know what the content of your Popover component is and how it's built and if it's relevant to any package.

  • Created another component called MyComponent with the following code:
import React, { useEffect } from "react"
import Wrapper from "./Wrapper"
import Popover from "./Popover"
const MyComponent = () => {
  useEffect(() => {
    if (typeof window === "undefined" || !window.document) {
      console.log(`bailing out of the useeffect. Going to continue to render??`)
      return
    }
  }, [])

  console.log(`reached before render`)
  return (
    <Wrapper>
        <div>first iteration</div>
    </Wrapper>
  )
}
export default MyComponent
  • Changed src\pages\index.js to the following:
import React from "react"
import MyComponent from '../components/MyComponent'
export default () => <div><MyComponent/></div>

  • As is, issued yarn build && yarn serve to generate a production build it went through and opening up http://localhost:9000 i'm presented with the following:
    vandesh_1

If you see on the developer tools on the right side, technically the console.log(reached before render), shouldn't be posted, but it did.

  • I changed the code on MyComponent.js to the following:
import React, { useEffect } from "react"
import Wrapper from "./Wrapper"
import Popover from "./Popover"
const MyComponent = () => {
  useEffect(() => {
    if (typeof window === "undefined" || !window.document) {
      console.log(`bailing out of the useeffect. Going to continue to render??`)
      return
    }
  }, [])

  console.log(`reached before render`)
  return (
    <Wrapper>
      <Popover
              placement="bottom-end"
              appendTo={document.body} /> 
    </Wrapper>
  )
}
export default MyComponent
```` 

- Issued again a production build and i'm presented with the following:
```bash
$ gatsby build
success open and validate gatsby-configs - 0.022s
success load plugins - 0.078s
success onPreInit - 0.006s
success delete html and css files from previous builds - 0.022s
success initialize cache - 0.011s
success copy gatsby files - 0.240s
success onPreBootstrap - 0.016s
success createSchemaCustomization - 0.004s
success source and transform nodes - 0.027s
success building schema - 0.216s
success createPages - 0.003s
success createPagesStatefully - 0.067s
success onPreExtractQueries - 0.002s
success update schema - 0.094s
success extract queries from components - 0.108s
success write out requires - 0.010s
success write out redirect data - 0.005s
success onPostBootstrap - 0.007s
⠀
info bootstrap finished - 6.134 s
⠀
success Building production JavaScript and CSS bundles - 4.820s
success Rewriting compilation hashes - 0.007s
success run queries - 5.003s - 1/1 0.20/s
[                            ]   0.001 s 0/1 0% Building static HTML for pages
failed Building static HTML for pages - 0.521s

 ERROR #95312

"document" is not available during server side rendering.

See our docs page for more info on this error: https://gatsby.dev/debug-html


  15 |       <Popover
  16 |               placement="bottom-end"
> 17 |               appendTo={document.body} />
     |                         ^
  18 |     </Wrapper>
  19 |     // <Wrapper>
  20 |


  WebpackError: ReferenceError: document is not defined

  - MyComponent.js:17 MyComponent
    src/components/MyComponent.js:17:25


error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

As you can see by the output, the issue manifests itself, reason why it's happening is that when Gatsby is trying to generate the build he reaches a point that it doesn't know what document and starts throwing the error.

  • Changing the MyComponent.js to the following:
import React, { useEffect } from "react"
import Wrapper from "./Wrapper"
import Popover from "./Popover"
const MyComponent = () => {
  useEffect(() => {
    if (typeof window === "undefined" || !window.document) {
      console.log(`bailing out of the useeffect. Going to continue to render??`)
      return
    }
  }, [])

  console.log(`reached before render`)
  return (

    <Wrapper>

        {typeof window!=='undefined'?<Popover
              placement="bottom-end"
              appendTo={document.body} />:<div>something to be displayed if the document is not present</div> }

    </Wrapper>
  )
}
export default MyComponent

And issuing back a production build for the third time, the build goes through and if i open http://localhost:9000 i'm presented with the following:
vandesh_2

Once again my take on this is that despite returning from the useEffect hook it doesn't mean you'll be "bailing out" from the entire functional component, it just means you're bailing out of a function that is inside the hook but the rest of the code flow will run through.

Ah! Thanks a lot @jonniebigodes . I did fix it eventually with a similar check like you added, but completely missed the fact that the return in the useEffect wouldn't return from the component itself. I do feel stupid and in search for coffee!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jimfilippou picture jimfilippou  ·  3Comments

andykais picture andykais  ·  3Comments

dustinhorton picture dustinhorton  ·  3Comments

3CordGuy picture 3CordGuy  ·  3Comments

theduke picture theduke  ·  3Comments