Carlos Araújo
Carlos Araújo

Reputation: 61

WebpackError: ReferenceError: document is not defined - netlify build

I am getting an error building my project on netlify (WebpackError: ReferenceError: document is not defined). In develop works fine!

I saw that is possible to solve this problem with JSDOM but i have tried and i couldn't make it right.

    import React from "react";
    import "../css/DarkMode.css";
    
    const DarkMode = () => {
      let clickedClass = "clicked";
      const body = document.body;
      const lightTheme = "light";
      const darkTheme = "dark";
      let theme;
    
      if (localStorage) {
        theme = localStorage.getItem("theme");
      }
    
      if (theme === lightTheme || theme === darkTheme) {
        body.classList.add(theme);
      } else {
        body.classList.add(lightTheme);
      }
    
      const switchTheme = (e) => {
        if (theme === darkTheme) {
          body.classList.replace(darkTheme, lightTheme);
          e.target.classList.remove(clickedClass);
          localStorage.setItem("theme", "light");
          theme = lightTheme;
        } else {
          body.classList.replace(lightTheme, darkTheme);
          e.target.classList.add(clickedClass);
          localStorage.setItem("theme", "dark");
          theme = darkTheme;
        }
      };
    
      return (
        <button
          className={theme === "dark" ? clickedClass : ""}
          id="darkMode"
          className={"btnDarkMode"}
          onClick={(e) => switchTheme(e)}
        ></button>
      );
    };
    
    export default DarkMode;

Upvotes: 3

Views: 3425

Answers (2)

mirik999
mirik999

Reputation: 389

You want to realize theme switching totally wrong.

  1. React uses Virtual DOM not a real dom. ( Virtual DOM is an Object version of HTML tree ) that is the reason why document is not defined before page init. If you want to get document it will be best practice to use it in useEffect() hook. ( after page init )
  2. In your case you have a lot of choice to realize multi theming , I would recommend to use styled-components

Upvotes: 1

Ferran Buireu
Ferran Buireu

Reputation: 29320

Extending @mirik999's answer:

It's not about jsdom per se. As it has been said, with React, you are creating and manipulating a virtual DOM (vDOM), and with your snippet, you are pointing directly to the real DOM (document.body and so on). In addition, these actions decreases drastically the performance of the code/application since they have a huge cost by the browser, that's why React is so fast (among other things).

Manipulating the real DOM in a React application may cause severe caveats and warnings, potentially blocking the hydration of React (the refreshing/re-rendering new content-on-demand).

The fact that your code works under gatsby develop and not in gatsby build is, summarizing, because gatsby develop is handled by the browser, where there are global objects (such as window or document). gatsby build id compiled in the Node server, where obviously there's no window or document because they are not even created yet.

The easy workaround is to wrap your code (where you are using global objects) in this condition:

if (typeof window !== `undefined`) { // or typeof document !== 'undefined'
  // your code that uses global objects here
}

But as I said, it may block the hydration of React.

To add theming in Gatsby, you have a bunch of options like using plugins (gatsby-plugin-dark-mode) or use a React-based approach.

Useful resources:

Upvotes: 2

Related Questions