skip to Main Content

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?

2

Answers


  1. 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');
    
    Login or Signup to reply.
  2. For TS folks struggling out there with clevertap and nextjs, install sdk and types:

    npm i -S clevertap-web-sdk @types/clevertap-web-sdk
    

    then, async import while initializing:

    import CleverTap from 'clevertap-web-sdk/clevertap';
    // ^^ this only imports types
    
    let clevertap: CleverTap;
    
    export const initClevertap = async (clevertapAccountID: string, region: string): Promise<void> => {
        clevertap = (await import('clevertap-web-sdk')).default;
        clevertap.init(clevertapAccountID, region);
        clevertap.privacy.push({ optOut: false });
        clevertap.privacy.push({ useIP: true });
        clevertap.setLogLevel(0);
        clevertap.spa = true;
    };
    

    PS: dynamic import didn’t worked for me, looks like it’s only for components and not libs

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search