249

In my Next.js app I can't seem to access window:

Unhandled Rejection (ReferenceError): window is not defined

componentWillMount() {
    console.log('window.innerHeight', window.innerHeight);
}

Enter image description here

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Leon Gaban
  • 36,509
  • 115
  • 332
  • 529
  • 1
    Move the code to componentDidMount() which is only executed on the client where with `window` is available. Besides, `componentWillMount()` is being [deprecated](https://reactjs.org/docs/react-component.html#unsafe_componentwillmount) in v17https://github.com/zeit/next.js/wiki/FAQ#i-use-a-library-which-throws-window-is-undefined – Alexander Staroselsky Mar 13 '19 at 20:59

24 Answers24

225

̶A̶n̶o̶t̶h̶e̶r̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶i̶s̶ ̶b̶y̶ ̶u̶s̶i̶n̶g̶ ̶p̶r̶o̶c̶e̶s̶s̶.̶b̶r̶o̶w̶s̶e̶r ̶ ̶t̶o̶ ̶j̶u̶s̶t̶ ̶e̶x̶e̶c̶u̶t̶e̶ ̶ ̶y̶o̶u̶r̶ ̶c̶o̶m̶m̶a̶n̶d̶ ̶d̶u̶r̶i̶n̶g̶ ̶r̶e̶n̶d̶e̶r̶i̶n̶g̶ ̶o̶n̶ ̶t̶h̶e̶ ̶c̶l̶i̶e̶n̶t̶ ̶s̶i̶d̶e̶ ̶o̶n̶l̶y̶.

But process object has been deprecated in Webpack5 and also NextJS, because it is a NodeJS variable for backend side only.

So we have to use back window object from the browser.

if (typeof window !== "undefined") {
  // Client-side-only code
}

Other solution is by using react hook to replace componentDidMount:

useEffect(() => {
    // Client-side-only code
})
Darryl RN
  • 7,432
  • 4
  • 26
  • 46
  • 2
    this approach is now already being **deprecated** https://github.com/zeit/next.js/issues/5354#issuecomment-520305040 – Anton Dozortsev May 05 '20 at 21:09
  • 29
    You mean `typeof window !== "undefined"`, because otherwise it is serverside run – Allwe Aug 24 '20 at 15:29
  • 6
    seriously, that guy needs to edit his comment – Robbie Cook Jan 17 '21 at 04:06
  • 10
    @Robbie - I got the other "guy" to edit the comment. – Guy Oct 06 '21 at 20:57
  • 1
    I've been developing a website in react all day. First project too and I was stuck on this all day. Thanks – Bender Feb 02 '22 at 11:49
  • why "undefined" as a string - I'm used to do this: `type(...) == list` which is python code - so typeof ... returns a string but why a string? – france1 Jan 06 '23 at 09:02
  • @france1 you can check the return value of typeof from here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof. I believe it is to avoid confusion, so they just return string undefined. – Darryl RN Jan 09 '23 at 01:56
100

If you use React Hooks you can move the code into the Effect Hook:

import * as React from "react";

export const MyComp = () => {

  React.useEffect(() => {
    // window is accessible here.
    console.log("window.innerHeight", window.innerHeight);
  }, []);

  return (<div></div>)
}

The code inside useEffect is only executed on the client (in the browser), thus it has access to window.

Dylan Pierce
  • 4,313
  • 3
  • 35
  • 45
Rotareti
  • 49,483
  • 23
  • 112
  • 108
97

Move the code from componentWillMount() to componentDidMount():

componentDidMount() {
  console.log('window.innerHeight', window.innerHeight);
}

In Next.js, componentDidMount() is executed only on the client where window and other browser specific APIs will be available. From the Next.js wiki:

Next.js is universal, which means it executes code first server-side, then client-side. The window object is only present client-side, so if you absolutely need to have access to it in some React component, you should put that code in componentDidMount. This lifecycle method will only be executed on the client. You may also want to check if there isn't some alternative universal library which may suit your needs.

Along the same lines, componentWillMount() will be deprecated in v17 of React, so it effectively will be potentially unsafe to use in the very near future.

Alan W. Smith
  • 24,647
  • 4
  • 70
  • 96
Alexander Staroselsky
  • 37,209
  • 15
  • 79
  • 91
82

With No SSR

https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr

import dynamic from 'next/dynamic'

const DynamicComponentWithNoSSR = dynamic(
  () => import('../components/hello3'),
  { ssr: false }
)

function Home() {
  return (
    <div>
      <Header />
      <DynamicComponentWithNoSSR />
      <p>HOME PAGE is here!</p>
    </div>
  )
}

export default Home
tanguy_k
  • 11,307
  • 6
  • 54
  • 58
Kate
  • 1,107
  • 8
  • 7
27

The error occurs because window is not yet available, while component is still mounting. You can access window object after component is mounted.

You can create a very useful hook for getting dynamic window.innerHeight or window.innerWidth

const useDeviceSize = () => {

  const [width, setWidth] = useState(0)
  const [height, setHeight] = useState(0)

  const handleWindowResize = () => {
    setWidth(window.innerWidth);
    setHeight(window.innerHeight);
  }

  useEffect(() => {
    // component is mounted and window is available
    handleWindowResize();
    window.addEventListener('resize', handleWindowResize);
    // unsubscribe from the event on component unmount
    return () => window.removeEventListener('resize', handleWindowResize);
  }, []);

  return [width, height]

}

export default useDeviceSize 

Use case:

const [width, height] = useDeviceSize();
Ismoil Shokirov
  • 2,213
  • 2
  • 16
  • 32
14

componentWillMount() lifecycle hook works both on server as well as client side. In your case server would not know about window or document during page serving, the suggestion is to move the code to either

Solution 1:

componentDidMount()

Or, Solution 2

In case it is something that you only want to perform in then you could write something like:

componentWillMount() {
    if (typeof window !== 'undefined') {
        console.log('window.innerHeight', window.innerHeight);
    }
}
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
11

In the constructor of your class Component you can add

if (typeof window === 'undefined') {
    global.window = {}
}

Example:

import React, { Component } from 'react'

class MyClassName extends Component {

    constructor(props){
        super(props)
        ...
        if (typeof window === 'undefined') {
            global.window = {}
        }
}

This will avoid the error (in my case, the error would occur after I would click reload of the page).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
RealScatman
  • 358
  • 3
  • 5
  • 1
    is there any documentation on `global`? – Jayen Oct 01 '20 at 09:13
  • 3
    I'd highly recommend against this approach. Setting `global.window` on the server-side will not behave the same as the actual `window` object provided in the browser environment. – juliomalves Jan 08 '22 at 19:44
  • 1
    Though this will avoid the error at hand on the client, it will create others you likely won't see or find very easily. To future travelers, heed @juliomalves' advice and avoid this approach. – Jason R Stevens CFA Nov 16 '22 at 23:06
11

Best solution ever

import dynamic from 'next/dynamic';

const Chart = dynamic(()=> import('react-apexcharts'), {
    ssr:false,
})
Vinicius Pollo
  • 105
  • 1
  • 3
  • 3
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 02 '21 at 16:06
  • Does this mean that this page is gonna get rendered in the browser instead of server ? for me the whole purpose of using next js is rendering in server – Shamseer Ahammed Jan 25 '22 at 14:47
  • Yes, because when generating static html content for our sites, there are no window/screen global objects we can access. – amerw Sep 15 '22 at 08:44
11

A bit late but you could also consider using Dynamic Imports from next turn off SSR for that component.

You can warp the import for your component inside a dynamic function and then, use the returned value as the actual component.

import dynamic from 'next/dynamic'

const BoardDynamic = dynamic(() => import('../components/Board.tsx'), {
  ssr: false,
})

<>
   <BoardDynamic />
</>
Parsa Safavi
  • 218
  • 2
  • 10
11

global?.window && window.innerHeight

It's important to use the operator ?., otherwise the build command might crash.

lucgauer
  • 125
  • 1
  • 8
7

I have to access the hash from the URL so I come up with this

const hash = global.window && window.location.hash;
U.A
  • 2,991
  • 3
  • 24
  • 36
5

Here's an easy-to-use workaround that I did.

const runOnClient = (func: () => any) => {
  if (typeof window !== "undefined") {
    if (window.document.readyState == "loading") {
      window.addEventListener("load", func);
    } else {
      func();
    }
  }
};

Usage:

runOnClient(() => {
// access window as you like
})

// or async
runOnClient(async () => {
// remember to catch errors that might be raised in promises, and use the `await` keyword wherever needed
})

This is better than just typeof window !== "undefined", because if you just check that the window is not undefined, it won't work if your page was redirected to, it just works once while loading. But this workaround works even if the page was redirected to, not just once while loading.

Roj
  • 995
  • 1
  • 8
  • 22
3

I was facing the same problem when i was developing a web application in next.js This fixed my problem, you have to refer to refer the window object in a life cycle method or a react Hook. For example lets say i want to create a store variable with redux and in this store i want to use a windows object i can do it as follows:

let store
useEffect(()=>{
    store = createStore(rootReducers,   window.__REDUX_DEVTOOLS_EXTENSION__ && 
    window.__REDUX_DEVTOOLS_EXTENSION__())
 }, [])
 ....

So basically, when you are working with window's object always use a hook to play around or componentDidMount() life cycle method

crispengari
  • 7,901
  • 7
  • 45
  • 53
3

I had this same issue when refreshing the page (caused by an import that didn't work well with SSR).

What fixed it for me was going to pages where this was occurring and forcing the import to be dynamic:

import dynamic from 'next/dynamic';


const SomeComponent = dynamic(()=>{return import('../Components/SomeComponent')}, {ssr: false});

//import SomeComponent from '../Components/SomeComponent'

Commenting out the original import and importing the component dynamically forces the client-side rendering of the component.

The dynamic import is covered in Nextjs's documentation here: https://nextjs.org/docs/advanced-features/dynamic-import

I got to this solution by watching the youtube video here: https://www.youtube.com/watch?v=DA0ie1RPP6g

Nick Weimer
  • 497
  • 5
  • 7
3

I wrapped the general solution (if (typeof window === 'undefined') return;) in a custom hook, that I am very pleased with. It has a similiar interface to reacts useMemo hook which I really like.

import { useEffect, useMemo, useState } from "react";

const InitialState = Symbol("initial");

/**
 *
 * @param clientFactory Factory function similiar to `useMemo`. However, this function is only ever called on the client and will transform any returned promises into their resolved values.
 * @param deps Factory function dependencies, just like in `useMemo`.
 * @param serverFactory Factory function that may be called server side. Unlike the `clientFactory` function a resulting `Promise` will not be resolved, and will continue to be returned while the `clientFactory` is pending.
 */
export function useClientSideMemo<T = any, K = T>(
  clientFactory: () => T | Promise<T>,
  deps: Parameters<typeof useMemo>["1"],
  serverFactory?: () => K
) {
  const [memoized, setMemoized] = useState<T | typeof InitialState>(
    InitialState
  );

  useEffect(() => {
    (async () => {
      setMemoized(await clientFactory());
    })();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps);

  return typeof window === "undefined" || memoized === InitialState
    ? serverFactory?.()
    : memoized;
}

Usage Example:

I am using it to dynamically import libaries that are not compatible with SSR in next.js, since its own dynamic import is only compatible with components.

  const renderer = useClientSideMemo(
    async () =>
      (await import("@/components/table/renderers/HighlightTextRenderer"))
        .HighlightTextRendererAlias,
    [],
    () => "text"
  );

As you can see I even implemented a fallback factory callback, so you may provide a result when initially rendering on the server aswell. In all other aspects this hook should behave similiar to reacts useMemo hook. Open to feedback.

tlt
  • 358
  • 4
  • 10
1

For such cases, Next.js has Dynamic Import.

A module that includes a library that only works in the browser, it's suggested to use Dynamic Import. Refer

San-Mak
  • 11
  • 1
  • 4
1

Date: 06/08/2021

Check if the window object exists or not and then follow the code along with it.

 function getSelectedAddress() {
    if (typeof window === 'undefined') return;

    // Some other logic
 }
Mohamed Jakkariya
  • 1,257
  • 1
  • 13
  • 16
1

For Next.js version 12.1.0, I find that we can use process.title to determine whether we are in browser or in node side. Hope it helps!

export default function Projects(props) {
    console.log({ 'process?.title': process?.title });

    return (
        <div></div>
    );
}

1. From the terminal, I receive { 'process?.title': 'node' }

process.title === 'node'

2. From Chrome devtool, I revice { 'process?.title': 'browser' }

process.title === 'browser'

W.K.C
  • 45
  • 1
  • 1
  • 8
0

You can define a state var and use the window event handle to handle changes like so.

const [height, setHeight] = useState();

useEffect(() => {
    if (!height) setHeight(window.innerHeight - 140);
    window.addEventListener("resize", () => {
        setHeight(window.innerHeight - 140);
    });
}, []);
Seth Samuel
  • 245
  • 1
  • 3
0

You can try the below code snippet for use-cases such as - to get current pathname (CurrentUrl Path)

 import { useRouter } from "next/router";

 const navigator = useRouter()
 console.log(navigator.pathname);
Inder Pal Singh
  • 380
  • 4
  • 16
0

For anyone who somehow cannot use hook (for example, function component):

Use setTimeout(() => yourFunctionWithWindow()); will allow it get the window instance. Guess it just need a little more time to load.

Terry Windwalker
  • 1,343
  • 2
  • 16
  • 36
0

Nowadays you can add the 'use client' directive at the top of your client side component.

https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive

jfunk
  • 7,176
  • 4
  • 37
  • 38
-1

I want to leave this approach that I found interesting for future researchers. It's using a custom hook useEventListener that can be used in so many others needs.

Note that you will need to apply a little change in the originally posted one, like I suggest here.

So it will finish like this:

import { useRef, useEffect } from 'react'

export const useEventListener = (eventName, handler, element) => {
  const savedHandler = useRef()

  useEffect(() => {
    savedHandler.current = handler
  }, [handler])

  useEffect(() => {
    element = !element ? window : element
    const isSupported = element && element.addEventListener
    if (!isSupported) return

    const eventListener = (event) => savedHandler.current(event)

    element.addEventListener(eventName, eventListener)

    return () => {
      element.removeEventListener(eventName, eventListener)
    }
  }, [eventName, element])
}

Lianel
  • 101
  • 10
-1

If it is NextJS app and inside _document.js, use below:

<script dangerouslySetInnerHTML={{
        __html: `
            var innerHeight = window.innerHeight;
        `
        }} />
Lakshit Nagar
  • 303
  • 3
  • 5