How to fix “Next.js: document is not defined”?

Next.js is a powerful React framework that allows developers to create server-side rendered (SSR) and static web applications. A common error encountered while working with Next.js is the “document is not defined” error. This error happens because the document is a client-side browser object, which isn’t available during server-side rendering. In this article, we’ll explain why this error occurs and how to fix it.

Table of Content

  • When does this error occur?
  • Fixing Document not defined
  • Detailed Code Explanation
  • Best practices to avoid document is not defined error
  • Conclusion

Steps to Setup a NextJS App

To create a new Next.js application, follow these steps:

Step 1: Create a demo Next.Js application running the following commands in the terminal.

npx create-next-app demoapp
cd demoapp

Step 2: In the terminal run the following command.

npm run dev

Project Structure

demoapp project structure

Verify the dependencies in package.json file.

 "dependencies": {
"react": "^18",
"react-dom": "^18",
"next": "14.2.3"
}

When does this error occur?

The document object is part of the browser’s Window interface and represents the DOM (Document Object Model) of the current web page. When Next.js renders pages on the server, there is no browser environment, so the document object is not defined. This results in the “document is not defined” error when code that references the document is executed on the server.

This error typically occurs in this scenario: Direct usage of document in the component’s render method or lifecycle methods.

JavaScript
//index.js

import Head from "next/head";

// This code will throw an error during SSR
const MyComponent = () => {
    const element = document.getElementById("some-id");
    return <div>{element ? element.textContent : "No element"}</div>;
};

export default function Home() {
    return (
        <>
            <Head>
                <title>Create Next App</title>
                <meta name="description"
                    content="Generated by create next app" />
                <meta name="viewport"
                    content="width=device-width, initial-scale=1" />
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <main>
                <div style={{
                    minHeight: "100vh", backgroundColor: "white"
                }}>
                    <MyComponent />
                </div>
            </main>
        </>
    );
}

document not defined error

Fixing Document not defined

Approach:

  • Use useEffect for Client-Side Code: The useEffect hook ensures that code accessing the document object only runs after the component mounts on the client side.
  • Conditional Checks for document: Before accessing document, perform a conditional check to ensure it exists.
  • Dynamic Imports for Client-Side Components: Dynamically import components that rely on the document object to prevent them from being rendered on the server.
  • Avoid Direct DOM Manipulation in SSR: Ensure that DOM manipulation code is only executed in the browser environment.
  • Utilize Conditional Rendering: Render components or perform actions conditionally based on the environment to ensure code runs in the correct context.

Conditional Checks for document

referencing to document during server-side rendering resulting in document not found error, so you need wrap the code that accesses document in a conditional check to ensure it only runs in the client-side environment.

JavaScript
// pages/index.js

import Head from "next/head";
import { useEffect, useState } from "react";

const MyComponent = () => {
    const [elementContent, setElementContent] = useState("No element");

    useEffect(() => {
        const element = document.getElementById("some-id");
        if (element) {
            setElementContent(element.textContent);
        }
    }, []);

    return (
        <div>
            <div id="some-id">Hello, geek!</div>
            <h2>{elementContent} hi</h2>
        </div>
    );
};

export default function Home() {
    const [docEnv, setDocEnv] = useState(false);

    useEffect(() => {
        if (typeof document !== "undefined") {
            setDocEnv(true);
        }
    }, []);

    return (
        <>
            <Head>
                <title>Create Next App</title>
                <meta name="description" content="Generated by create next app" />
                <meta name="viewport" content="width=device-width, initial-scale=1" />
                <link rel="icon" href="/favicon.ico" />
            </Head>
            <main>
                <div
                    style={{
                        minHeight: "100vh",
                        backgroundColor: "white",
                        color: "black",
                    }}
                >
                    {docEnv && <MyComponent />}
                </div>
            </main>
        </>
    );
}

The useEffect hook ensures that the code inside it runs only on the client side after the component has mounted. This means that the document object is available, allowing us to use document.getElementById to get an element’s content and display it.

solved document not error code output

Detailed Code Explanation

  • Using useEffect for Client-Side Execution: The useEffect hook in MyComponent ensures that the code accessing the document object only runs after the component mounts. This avoids server-side rendering issues, as document is not defined on the server.
  • Accessing and Displaying Element Content: Inside the useEffect, document.getElementById(“some-id”) fetches the div element with id=”some-id”. If the element exists, its text content is retrieved and stored in the elementContent state. The content is then displayed inside an h2 tag, allowing dynamic updates based on the element’s content.
  • Conditional Rendering: In the Home component, docEnv is set to true only if document is defined, ensuring that MyComponent is only rendered on the client side.’

Best practices to avoid document is not defined error

  • Use useEffect to Run Client-Side Code: Ensure that code dependent on the document object is executed only on the client side by wrapping it in a useEffect hook.
  • Check if document is Defined Before Accessing It: Perform a conditional check to confirm the existence of the document object.
  • Use Dynamic Imports to Load Client-Side Components: Dynamically import components that rely on the document object to prevent them from being rendered on the server.
  • Avoid Direct DOM Manipulation in SSR: Ensure that DOM manipulation code is only executed in the browser environment.
  • Utilize Conditional Rendering: Render components or perform actions conditionally based on the environment to ensure code runs in the correct context.

Conclusion

“document is not defined” error in Next.js occurs because the document object is only available in the browser, not during server-side rendering. To avoid this error, use the useEffect hook to run client-side code, check if document is defined before accessing it, and use dynamic imports for client-side components. These practices ensure your application works seamlessly both on the server and in the browser.