Skip to content
Advertisement

Server Error ReferenceError: window is not defined in Next.js

I’m trying to integrate CleverTap into my Next.js app. Followed the documentation Web SDK Quick Start Guide but facing issue:

Server Error ReferenceError: window is not defined in Next.js

Please have a look


_app.tsx

import React, { useEffect, useState } from "react";
import type { AppProps } from "next/app";
import { appWithTranslation } from "next-i18next";
import { Hydrate, QueryClient, QueryClientProvider } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";

import nextI18NextConfig from "../next-i18next.config.js";

import "tailwindcss/tailwind.css";
import "styles/globals.scss";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import { useRouter } from "next/router";
import SvgPageLoading from "components/color-icons/PageLoading";
// import { PageLoading } from "components/color-icons/";

import { DefaultSeo } from 'next-seo';
import SEO from 'next-seo.config';
import {cleverTap} from "utils/cleverTapHelper";

cleverTap.initialize('TEST-61c-a12');

function MyApp({ Component, pageProps }: AppProps) {
  const [queryClient] = React.useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            refetchOnWindowFocus: false,
            staleTime: Infinity,
          },
        },
      })
  );
  const router = useRouter();
  const [isAnimating, setIsAnimating] = useState(false);
  useEffect(() => {
    const handleStart = () => {
      setIsAnimating(true);
    };
    const handleStop = () => {
      setIsAnimating(false);
    };

    router.events.on("routeChangeStart", handleStart);
    router.events.on("routeChangeComplete", handleStop);
    router.events.on("routeChangeError", handleStop);

    return () => {
      router.events.off("routeChangeStart", handleStart);
      router.events.off("routeChangeComplete", handleStop);
      router.events.off("routeChangeError", handleStop);
    };
  }, [router]);
  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={pageProps.dehydratedState}>
        <DefaultSeo {...SEO} />
        <Component {...pageProps} />
        {isAnimating && (
          <div className="fixed top-0 left-0 flex items-center justify-center w-screen h-screen overflow-visible bg-white bg-opacity-80 z-overlay top-z-index">
            <SvgPageLoading />
          </div>
        )}
        <ReactQueryDevtools initialIsOpen={false} />
      </Hydrate>
    </QueryClientProvider>
  );
}

export default appWithTranslation(MyApp, nextI18NextConfig);

cleverTapHelper.ts

export const cleverTap = {
    initialize: function (accountId) {
        console.log('I see initialize req')
        window.clevertap = {event: [], profile: [], account: [], onUserLogin: [], notifications: []};
        window.clevertap.account.push({'id': accountId});
        (function () {
            var wzrk = document.createElement('script');
            wzrk.type = 'text/javascript';
            wzrk.async = true;
            wzrk.src = ('https:' == document.location.protocol ? 'https://d2r1yp2w7bby2u.cloudfront.net' : 'http://static.clevertap.com') + '/js/a.js';
            var s = document.getElementsByTagName('script')[0];
            s.parentNode.insertBefore(wzrk, s);
        })();
    },

    event: function (name, payload = {}) {
        console.log('I see event req')
        if (payload) {
            window.clevertap.event.push(name, payload);
        } else {
            window.clevertap.event.push(name);
        }

    },

    profile: function (payload) {
        console.log('I see profile req')
        window.clevertap.profile.push(payload);
    },

    logout: function () {
        console.log('I see logout req')
        window.clevertap.logout();
    }
};

cleverTap.d.ts

declare global {
    interface Window {
        clevertap: any;
    }
}

export {};

Window object should not be undefined but getting undefined! What’s going on?

Advertisement

Answer

This is because NextJS is trying to execute that function on the server because it uses SSR, and window is a browser object. Since the window object is available only in the browser (client-side), the server is unable to identify the window object, hence getting undefined. In order to fix this, you should make sure that any functions/components that contain client-side related code be executed only on the browser or client-side. One way is using hooks such as useEffect that run only after the component is mounted. Another way is to use lazy loading which pretty much does the same thing.

  1. Using useEffect hook. In your _app.tsx component, add a new useEffect hook and move the initialization code into the newly created useEffect function.
useEffect(()=>{
  cleverTap.initialize('TEST-61c-a12');
},[])
  1. Using lazy loading. (Dynamic import) Instead of directly importing the function, import it dynamically and set server-side rendering to false:
import dynamic from 'next/dynamic'

const cleverTap = dynamic(()=>{
  return import("utils/cleverTapHelper")
},
{ssr:false}
)

cleverTap.initialize('TEST-61c-a12');
Advertisement