Next.js Discord

Discord Forum

'window not defined' when trying to access it inside a Context

Answered
MaKTaiL posted this in #help-forum
Open in Discord
Avatar
I'm building a sidebar and I created a SideBarContext to track isSideBarOpen variable. I also want to set a default value based on window size.

import { createContext, useState } from "react";

export const SideBarContext = createContext({
  sideBarOpen: false,
  setSideBarOpen: (sideBarOpen: boolean) => {},
  toggleSideBar: () => {},
});

export function SideBarProvider({ children }: { children: React.ReactNode }) {
  const [sideBarOpen, setSideBarOpen] = useState(
    window.innerWidth < 1280 ? false : true,
  );

  const toggleSideBar = () => {
    setSideBarOpen(!sideBarOpen);
  };

  return (
    <SideBarContext.Provider
      value={{ sideBarOpen, setSideBarOpen, toggleSideBar }}
    >
      {children}
    </SideBarContext.Provider>
  );
}


Everything is working just fine but I keep getting this error on the console server:
ReferenceError: window is not defined
. I tried adding "use client" to the context file but the error still comes up. The weird thing is that the default value behavior is working as intended. Context appears to run server side for some reason. Does anybody have any clue on how to solve this?
Answered by MaKTaiL
@European sprat Ok, I figured out how to fix this. It was a combination of two things:

1. First I moved the useState logic inside a useLayoutEffect (useEffect wasn't enough)

useLayoutEffect(() => {
    if (window.innerWidth >= 1280) {
      setSideBarOpen(true);
    }
}, []);


2. Second I used the dynamic library from "next/dynamic" inside the providers.tsx to import the SideBarContext:

"use client";

import { SessionProvider } from "next-auth/react";
import dynamic from "next/dynamic";

export default function Providers({ children }: { children: React.ReactNode }) {
  const SideBarProvider = dynamic(() =>
    import("@/contexts/SideBarContext").then((mod) => mod.SideBarProvider),
  );

  return (
    <SessionProvider>
      <SideBarProvider>
          {children}
      </SideBarProvider>
    </SessionProvider>
  );
}


Now it is working just as intended!
View full answer

3 Replies

Avatar
European sprat
needs to be a client component and you can't use window in useState like that because it gets prerendered on the server where there's no window, you'll need to set it in useEffect
Avatar
It is a client component. I tried using useEffect but there is this weird initial state when refreshing the page where the sidebar is either opened or closed when it should be otherwise.
Avatar
@European sprat Ok, I figured out how to fix this. It was a combination of two things:

1. First I moved the useState logic inside a useLayoutEffect (useEffect wasn't enough)

useLayoutEffect(() => {
    if (window.innerWidth >= 1280) {
      setSideBarOpen(true);
    }
}, []);


2. Second I used the dynamic library from "next/dynamic" inside the providers.tsx to import the SideBarContext:

"use client";

import { SessionProvider } from "next-auth/react";
import dynamic from "next/dynamic";

export default function Providers({ children }: { children: React.ReactNode }) {
  const SideBarProvider = dynamic(() =>
    import("@/contexts/SideBarContext").then((mod) => mod.SideBarProvider),
  );

  return (
    <SessionProvider>
      <SideBarProvider>
          {children}
      </SideBarProvider>
    </SessionProvider>
  );
}


Now it is working just as intended!
Answer