Next.js: document is not defined

64,318

Solution 1

I think, in server rendering mode, the document is undefined. You should be able to use it inside class lifecycle methods or useEffect

import React, {useEffect} from "react";
import {Elements, StripeProvider} from 'react-stripe-elements';
import CheckoutForm from '../../components/Payment/CheckoutForm';
import { useRouter } from 'next/router';


var stripe_load = () => {
    var aScript = document.createElement('script');
    aScript.type = 'text/javascript';
    aScript.src = " https://js.stripe.com/v3/";

    document.head.appendChild(aScript);
    aScript.onload = () => {

    };
};

function Payment({host}) {
    const key = host.includes('localhost') ? 'test' : 't';

    useEffect(() => {
      var aScript = document.createElement('script');
       aScript.type = 'text/javascript';
       aScript.src = " https://js.stripe.com/v3/";

       document.head.appendChild(aScript);
       aScript.onload = () => {

       };
    }, [])
    //stripe_load();

    const router = useRouter();

    return (
        <div className="Payment Main">
            <StripeProvider apiKey={key}>
                <Elements>
                    <CheckoutForm planid={router.query.id}/>
                </Elements>
            </StripeProvider>
            <br/>
            <br/>
            <p>Powered by Stripe</p>
        </div>
    );
};


Payment.getInitialProps = async ctx => {
    return { host: ctx.req.headers.host }
};

export default Payment

Solution 2

You need to wrap your document using validator process.browser, because this document is belong to client side, and the error occured when nextjs render in server side.

var stripe_load = () => {
    if (process.browser) {
        var aScript = document.createElement('script');
        aScript.type = 'text/javascript';
        aScript.src = " https://js.stripe.com/v3/";

        document.head.appendChild(aScript);
        aScript.onload = () => {

        };
    }
};

Solution 3

for me, this error occurs from lots of mistakes: first, you have to use

if (typeof window !== "undefined") {

for every window. and document. and especially for localStorage. functions (my self forgot to use this if clause for localStorage.getItem and the error didn't fix)

another problem was the import of the line below which was totally wrong!:

import {router} from "next/client";

Solution 4

To access the document in Next.js you need await the page render first then get it in a variable, like so:

const [_document, set_document] = React.useState(null)

React.useEffect(() => {
    set_document(document)
}, [])

Solution 5

You should read the difference between JS in node.js and JS in Browser. In node.js, you don't have DOM APIs (window, document, document.getElementById,...), the thing can be only have when your HTML is rendered in a thing called windows of Browsers. So next.js use node.js to run JS code and take result to render HTML file. But in node.js, nothing is windows of Browser.

My English is quite bad. But I hope this answer will be helpful for you.

Share:
64,318
Chris Hansen
Author by

Chris Hansen

Updated on July 09, 2022

Comments

  • Chris Hansen
    Chris Hansen almost 2 years

    I am trying to create a payment form where people can pay but I keep getting this error.

    document is not defined

    I'm using Next.js. Please see my code below:

    import React from "react";
    import {Elements, StripeProvider} from 'react-stripe-elements';
    import CheckoutForm from '../../components/Payment/CheckoutForm';
    import { useRouter } from 'next/router';
    
    
    var stripe_load = () => {
        var aScript = document.createElement('script');
        aScript.type = 'text/javascript';
        aScript.src = " https://js.stripe.com/v3/";
    
        document.head.appendChild(aScript);
        aScript.onload = () => {
    
        };
    };
    
    function Payment({host}) {
        const key = host.includes('localhost') ? 'test' : 't';
    
        stripe_load();
    
        const router = useRouter();
    
        return (
            <div className="Payment Main">
                <StripeProvider apiKey={key}>
                    <Elements>
                        <CheckoutForm planid={router.query.id}/>
                    </Elements>
                </StripeProvider>
                <br/>
                <br/>
                <p>Powered by Stripe</p>
            </div>
        );
    };
    
    
    Payment.getInitialProps = async ctx => {
        return { host: ctx.req.headers.host }
    };
    
    export default Payment