Window is not defined in Next.js React app
Asked Answered
J

30

296

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

Judaic answered 13/3, 2019 at 20:53 Comment(1)
Move the code to componentDidMount() which is only executed on the client where with window is available. Besides, componentWillMount() is being deprecated in v17https://github.com/zeit/next.js/wiki/FAQ#i-use-a-library-which-throws-window-is-undefinedTarr
T
101

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.

Tarr answered 13/3, 2019 at 21:1 Comment(0)
G
255

̶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
})
Grobe answered 16/3, 2019 at 11:46 Comment(9)
this approach is now already being deprecated github.com/zeit/next.js/issues/5354#issuecomment-520305040Crowder
You mean typeof window !== "undefined", because otherwise it is serverside runActinon
seriously, that guy needs to edit his commentAmiens
@Robbie - I got the other "guy" to edit the comment.Barney
I've been developing a website in react all day. First project too and I was stuck on this all day. ThanksAquarius
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?Chatty
@Chatty you can check the return value of typeof from here developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. I believe it is to avoid confusion, so they just return string undefined.Grobe
useEffect hook works.Voroshilovgrad
I've crafted an actual complete answer below, with the different patterns you can use depending on the situation https://mcmap.net/q/99931/-window-is-not-defined-in-next-js-react-appSybilsybila
E
113

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.

Explanation answered 19/12, 2019 at 3:42 Comment(2)
This seems to be the only way to do it properly in a react functional component.Efflorescence
This works although if you are doing something inside useEffect that updates state make sure to add an empty [] as deps.Nurseryman
T
101

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.

Tarr answered 13/3, 2019 at 21:1 Comment(0)
I
90

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
Ion answered 9/9, 2019 at 5:28 Comment(5)
This one works even if the library is accessing 'window' while is importedPraiseworthy
how to access the variable from external js? like var myVar = "bla"Muriate
@Muriate unfortunately you can only use dynamic import on component (and not functions)Hillock
TypeError: Cannot call a class as a functionOgdon
This works, but it is not a good approach if initial loading time is concerned. With no SSR, the website initial loading time is increased. Hence, SEO is badly impacted, as an SEO expert, I would not recommend thisKitten
O
30

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();
Offensive answered 24/7, 2021 at 10:45 Comment(6)
Clever. Very nice.Pool
It will work every time when you resize the window, it is useful in cases when you are working with css -> to make your pages responsiveOffensive
awesome solution. generic and reusable across everywhereBondon
@user884321 it will print correct width and height from the first render. You can check it here: codesandbox.io/s/weathered-water-575xy6?file=/src/App.jsOffensive
@IsmoilShokirov prnt.sc/2w7vA1Ga2IIv ?September
this returns 0 as window width/height on initial call which can cause unexpected behaviour. i have provided an answer to this question below which fixes this problem.September
P
16

global?.window && window.innerHeight

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

Pontonier answered 2/5, 2022 at 15:36 Comment(1)
That helped me in my React App in Hooks. Thanks a lot.Beaton
P
15

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);
    }
}
Patrica answered 14/3, 2019 at 5:28 Comment(0)
Y
13

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 />
</>
Yevette answered 16/2, 2022 at 21:44 Comment(1)
Heads up, in the current version of nextJS it's ssr, not SSRHoiden
H
11

Best solution ever

import dynamic from 'next/dynamic';

const Chart = dynamic(()=> import('react-apexcharts'), {
    ssr:false,
})
Hemispheroid answered 2/12, 2021 at 15:43 Comment(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.Cassell
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 serverThelma
Yes, because when generating static html content for our sites, there are no window/screen global objects we can access.Isomagnetic
F
10

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).

Femme answered 25/11, 2019 at 17:42 Comment(3)
is there any documentation on global?Stardom
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.Upolu
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.Kenn
A
8

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

const hash = global.window && window.location.hash;
Aster answered 7/3, 2021 at 5:4 Comment(2)
the neatest solution yetSquish
this solution works too, also you could just define it inside useEffect()Offensive
P
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.

Peptize answered 18/10, 2021 at 10:35 Comment(5)
This will override window.onload every timeFincher
@Fincher nope, if you can see it sets window.onload only if the page is not loaded.Peptize
consider this snippet runOnClient(f1); runOnClient(f2)Fincher
also read about this #2415250Fincher
@Fincher I made it append the listener, I hope it is fixed now.Peptize
M
5

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

Monarski answered 14/7, 2022 at 5:48 Comment(0)
B
5

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.

Bellicose answered 31/7, 2022 at 13:12 Comment(0)
W
4

Next JS 13 with Pages router

Below is a code snippet on how you can create, update(use create fn for updating as well), and remove a key (here username) from localStorage


const [username, setUsername] = useState<null | string>(null);

useEffect(() => {
  if (typeof window !== "undefined" && window.localStorage) {
    let token = localStorage.getItem("username");
    setUsername(token);
  }
}, [setUsername]);

const createItem = (newUsername: string) => {
  if (typeof window !== "undefined" && window.localStorage) {
    localStorage.setItem("username", newUsername);

    let token = localStorage.getItem("username");

    setUsername(token);
  }
};

const removeItem = () => {
  if (typeof window !== "undefined" && window.localStorage) {
    localStorage.removeItem("username");
    setUsername(null);
  }
};

console.log(username, "username");

Reference

Wiltshire answered 26/10, 2023 at 18:48 Comment(0)
K
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

Koetke answered 14/10, 2020 at 18:3 Comment(0)
S
3

None of the answers here is fully complete and most people will end up here because they hit the same issue but in various situations that require different solutions, so let me share the summary extracted from my paid course on the topic. I've also written a more detailed and free article.

Original question mentions an older version of React, using callbacks, but my answer is based on hooks and more relevant to current versions of React (16+) and Next (12+).

My component needs a browser-only library like Leaflet or jQuery, it crashes on server import

These libraries crash when being imported server-side, because they expect th window object to be immediately available.

This is a situation similar to the one described in this question.

You need what I call a Browser Component, a React component that cannot be prerendered on the server, contrary to Client components that are prerendered on the server in Next/React.

Solution: use lazy loading with next/dynamic and ssr: false to import your Browser Component. Now your component will only be imported and run in the browser.

My component needs to use the window object or any other browser-only code to render. Sadly, I cannot modify its code.

It's a Browser Component. Ideally, it should be rewritten to be a Client Component, but since you can't control the code you may not be able to do that.

You can use a NoSsr component. You can also use the useMounted hook (Next doc calls it "useClient") in the parent, but only if the parent is a Client Component. So NoSsr is a better choice in this case.

Relevant utility code:

export const useMounted = () => {
    const [mounted, setMounted] = useState<boolean>()
    // effects run only client-side
    // so we can detect when the component is hydrated/mounted
    // @see https://react.dev/reference/react/useEffect
    useEffect(() => {
        setMounted(true)
    }, [])
    return mounted
}

export function NoSsr({ children }: { children: React.ReactNode }) {
  const mounted = useMounted();
  if (!mounted) return null;
  return <>{children}</>;
}

My component needs to use the window object or any other browser-only code to render. I can modify its code.

The component should be a Client Component. Make it safe to be prerendered on the server

You can use the useMounted hook above to rewrite your component and render conditionally: you can use the window object to render only if the component is mounted.

This will turn your "Browser Component" into a "Client Component" that is safe for server-side rendering, even if renders nothing server-side or a loader.

My component needs a library that can be imported server-side but not used there like D3.

More broadly, this is the case where you want to do imperative modification of the DOM, and the closest to OP original question.

Make it a Client Component.

You can wrap offending code in an effect like so: useEffect(() => { console.log(window.innerHeight) }, []).

It will log/render nothing, or a loader, during server-side rendering, but that's totally fine.

My component code is exactly the same, but behaves slightly differently in browser and server.

This happens when using dates or localization features of JavaScript, you have the same code client-side and server-side but it generates a different result.

It should be a client component.

You can use suppressHydrationWarning or a Suspense as demonstrated in this article

Sybilsybila answered 27/11, 2023 at 11:3 Comment(0)
E
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

Epileptic answered 5/7, 2021 at 19:51 Comment(0)
A
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
 }
Aerator answered 6/8, 2021 at 15:15 Comment(0)
I
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'

Informer answered 7/5, 2022 at 20:51 Comment(0)
G
1

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);
Guertin answered 19/7, 2022 at 15:22 Comment(0)
N
1

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

Northeaster answered 21/7, 2023 at 19:34 Comment(0)
M
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);
    });
}, []);
Macdougall answered 1/9, 2021 at 14:8 Comment(0)
T
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.

Tical answered 27/8, 2022 at 12:49 Comment(1)
Actually, besides being not nice code and maybe have to use state to get it working, it did not work for me, still getting window not defined. Node has it's own setTimeout-definition which doesn't know the window object I solved with 'use client' and binding this to the functionPahl
P
0

This was driving me nuts, using useEffect is only working if you define your function within it. But I don't want to define helper functions there, so I ended up doing it like this:

    'use client'
    const MyClientC = () => { 
    const desiredCalculation = helperFN.bind(this)
       ...
    }
    // helperFN.ts
    interface Window {
      innerHeight: number
      ...
    
    }
    const isBrowser = () => typeof window !== 'undefined';
    let Window: Window = {} as Window;
    if (typeof window !== 'undefined') {
      Window = window;
    }
    
    const helperFn = () => {     
        if (!isBrowser()) {
          return false;
        }
        return Window.innerHeight ...

Maybe this helps

Pahl answered 13/10, 2023 at 11:13 Comment(0)
S
0

Solution when using hooks. Credit to @luckgaer.

This hook will return null on initial(server) render when called in a client component, and therefore not throw the above stated error. All we have to do to utilize the hook in a client component is to check that it's not null and skip its usage on its first render. Example on how to use the hook in a client component is below the hook snippet.

"use client";
import { useLayoutEffect, useState } from "react";

const useWindowWidth = (): number | null => {
  if (!global?.window) return null;
  const [windowWidth, setWindowWidth] = useState(window.innerWidth);

  useLayoutEffect(() => {
    let timeoutId: NodeJS.Timeout;

    const handleResize = () => {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        setWindowWidth(window.innerWidth);
      }, 100);
    };

    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return windowWidth;
};

export default useWindowWidth;

How to use in client components:

const windowWidth = useWindowWidth();
if (windowWidth && windowWidth >= 850)...
September answered 9/11, 2023 at 21:36 Comment(0)
P
0

Current answer as per next 13

In my case I was importing a library that used window in it's source code without adhering to it's usage in nextjs environments.

The error in such scenarios occurs because next13 tries to pre-render your pages and while doing so when they parse their code then obviously they will raise the error belonging to class things on the browser are not defined on the server.

Easy way to solve this is to tell nextjs not to pre-render that page. Simply opt out. In order to do that from your page.tsx export export const dynamic = "force-dynamic"; which omits this page from pre-rendering.

Another way would be to raise an issue and patch the source code yourself to then create a PR to be merged.

Final code

import React from "react";

function page() {
    return (
        <div> {/*tsx/jsx*/} </div>
    );
}

export const dynamic = "force-dynamic";
export default page;
Parliamentary answered 24/1 at 6:41 Comment(0)
B
-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])
}

Bloodstained answered 30/11, 2021 at 22:18 Comment(0)
G
-1

Just put 'use client' on the top of ur codebase

Gigantopithecus answered 1/2 at 0:45 Comment(0)
F
-3

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

<script dangerouslySetInnerHTML={{
        __html: `
            var innerHeight = window.innerHeight;
        `
        }} />
Fini answered 16/12, 2021 at 10:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.