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
_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.
- Using
useEffect
hook. In your_app.tsx
component, add a newuseEffect
hook and move the initialization code into the newly createduseEffect
function.
useEffect(()=>{ cleverTap.initialize('TEST-61c-a12'); },[])
- 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');