How to fix errors like ReferenceError Window is Not Defined in Remix Powered Web App | JavaScript Frameworks

Monday, February 14, 2022

Remix Run is a Server Side Rendering a.k.a. SSR first full-stack web framework. It focuses on the user interface and emphasizes web fundamentals to build fast, slick, and resilient user experiences.

Due to its SSR first approach, it runs the code both on server and client. That is why, many times, we might face an error like ReferenceError: window is not defined when we try to access the client-side only variables on the server. Let us consider an example --

const Screen = () => {
  const [innerWidth, setInnerWidth] = useState(window.innerWidth);

  ...
};

export default Screen;

In the above code snippet, we have a component named Screen that uses window.innerWidth to set the initial value. Now, during SSR, the code executes on the server but the window object simply doesn't exist there. Hence, this will break our app. To fix this we can move the access to the window variable inside useEffect. As useEffect code is only executed on the client.

import { useEffect } from 'react';

const Screen = () => {
  const [innerWidth, setInnerWidth] = useState(0);

  useEffect(() => {
    setInnerWidth(window.innerWidth);
  }, []);

  ...
};

...

Handling Third-Party Packages

Let us say we are using a package that internally consumes client/browser only code/variables. We can't modify the package code in any way. So, how to handle such cases?

In our programming questions page, we are using Ace Editor. On initialisation, it uses the window variable. So, it used to break our app during SSR. In Remix, if we want to consume certain code in the browser only then we can add .client to the file name and Remix will not render that file during SSR. Let us see it in action.

// file: CodeEditor.client.js

import AceEditor from "react-ace";

import "ace-builds/src-noconflict/mode-javascript";
import "ace-builds/src-noconflict/theme-monokai";
import "ace-builds/src-noconflict/ext-language_tools";
import "ace-builds/src-noconflict/ext-searchbox";
import "ace-builds/src-noconflict/keybinding-vscode";

const CodeEditor = (props) => {
  return <AceEditor {...props} />;
};

export default CodeEditor;

In the above code snippet, we created a file CodeEditor.client.js. Due to the .client extension, Remix will not render this file on the server.

// file: Question.js

import CodeEditor from "./CodeEditor.client";

const Question = () => {
  return <CodeEditor />;
};

export default Question;

We still need to import the CodeEditor in our Question Page. However, creating an instance of it via <CodeEditor /> would break our app.

Adding a Document Guard

We can fix the above issue by adding a check on the document variable and rendering a fallback UI.

// file: Question.js

import CodeEditor from "./CodeEditor.client";

const Fallback = () => {
  return <div>Loading IDE...</div>;
};

const Question = () => {
  return typeof document !== "undefined" ? <CodeEditor /> : <Fallback />;
};

...

Now, initially, we would be showing the user a fallback screen stating Loading IDE..., as soon as the scripts are loaded, Fallback would be replaced with the actual code editor.

Other Approaches

We can also use third party packages like Remix Utils to handle the browser only code. Remix Utils provides a component named ClientOnly that lets us render the children element only on the client-side. Let us modify our code to see how we can use this package.

npm i remix-utils
// file: Question.js

import CodeEditor from "./CodeEditor.client";

const Fallback = () => {
  return <div>Loading IDE...</div>;
};

const Question = () => {
  return (
    <ClientOnly fallback={<Fallback />}>
      <CodeEditor />
    </ClientOnly>
  );
};

...

The rendering flow would look like this:

  • Server-Side Rendering: Always render the fallback.
  • First time Client Side Rendering: Always render the fallback.
  • Client-Side Rendering Update: Replace with the actual component.
  • Client-Side Rendering Future Updates: Always use the actual component.

So, this is how we can resolve the ReferenceError: window is not defined error in Remix powered Web-App.

I hope this blog post helped you in some way. Please do share it and show our content much-needed love! :D

If you feel something is missing/wrong/improvement is possible then feel free to reach out -- Devtools Tech and Yomesh Gupta

Unsure about your interview prep? Practice Mock Interviews with us!

Book Your Slot Now