Fixing ‘self’ is not defined error in Next.js when importing client-side libraries

Updated: January 2, 2024 By: Guest Contributor Post a comment

Understanding the ‘self’ Reference Error in Next.js

When you encounter the Next.js ReferenceError: 'self' is not defined, it is usually because there is an attempt being made to use browser-specific objects or APIs during server-side rendering (SSR), which Next.js uses by default for pages. The variable self is typically associated with the window or worker context in a browser environment, but because this does not exist in a Node.js environment, the error is thrown.

Solving the ‘self’ is not defined Error

The most straightforward way to resolve this error is to ensure that your browser-only code is not executed during the server-side rendering process. Next.js provides a few ways to do this, considering its architecture that supports both server and client-side execution.

Dynamically Import with No SSR

Next.js offers the ability to import modules dynamically with the option to disable server-side rendering for those modules. By doing this, you import the library only on the client side, where self is defined. Here is how you can achieve this:

import dynamic from 'next/dynamic';

const ClientSideLibraryComponent = dynamic(
  () => import('path-to-client-library'),
  { ssr: false }
);

This tells Next.js to only import the client-side library on the client. After that, you can use ClientSideLibraryComponent anywhere in your components, exactly as you would use any other React component.

Conditional Use of ‘self’

If dynamically importing the library is not an option, you can conditionally use browser globals like self by checking the runtime context.

if (typeof window !== 'undefined') {
  // This code will run only in the browser because 'window' is not defined in Node
  const self = window.self;
  // You can now safely use 'self'
}

This approach ensures that your references to self or any other browser-specific global do not execute during SSR.

Using useEffect Hook

Another way to ensure code runs only on the client side is by using the React useEffect hook. Code inside useEffect will only run after the component has mounted on the client side. You can therefore use browser-specific objects within useEffect.

import { useEffect } from 'react';

const MyComponent = () => {
  useEffect(() => {
    // Any code here will only run on the client
    const self = window.self;
    // You can use 'self' here
  }, []);

  return "Welcome to Sling Academy!";
};

In all of these methods, you’re effectively splitting your code execution paths between the server and client. By doing so, you’re ensuring that browser-dependent code is not mistakenly run on the server where it’s not applicable.