Next.js
Ezoicβs standalone script is designed for traditional multi-page sites. Since Next.js uses client-side navigation, we must manually trigger Ezoic to scan the DOM and cleanup placeholders.
Global Types π
Create types/ezoic.d.ts to ensure type safety across your components.
declare global {
interface Window {
ezstandalone?: {
cmd: Array<() => void>;
showAds: (...ids: number[]) => void;
destroyPlaceholders: (...ids: number[]) => void;
};
}
}
export {};
Global Script Initialization π
Add the scripts to app/layout.tsx. Initializing the cmd queue here ensures that any subsequent calls to Ezoic are buffered until the library is fully loaded.
import Script from "next/script";
import EzoicRouteHandler from "@/components/EzoicRouteHandler";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<Script
id="ezoic-cmp"
src="https://cmp.gatekeeperconsent.com/min.js"
strategy="beforeInteractive"
data-cfasync="false"
/>
<Script
id="ezoic-cmp-2"
src="https://the.gatekeeperconsent.com/cmp.min.js"
strategy="beforeInteractive"
data-cfasync="false"
/>
<Script
id="ezoic-sa"
src="//www.ezojs.com/ezoic/sa.min.js"
strategy="beforeInteractive"
/>
<Script id="ezoic-init" strategy="beforeInteractive">
{`
window.ezstandalone = window.ezstandalone || {};
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
`}
</Script>
<Script
id="ezoic-analytics"
src="//ezoicanalytics.com/analytics.js"
strategy="beforeInteractive"
/>
</head>
<body>
<EzoicRouteHandler />
{children}
</body>
</html>
);
}
runEzoic Helper π
Create lib/ezoic.ts. This safely pushes functions to the Ezoic command queue, preventing errors if the script hasn't initialized when a component mounts.
export function runEzoic(fn: () => void) {
if (typeof window === "undefined") return;
window.ezstandalone = window.ezstandalone || {};
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
window.ezstandalone.cmd.push(fn);
}
Ad Placeholder Component π
This component manages the lifecycle of a single ad slot. It ensures the ad is displayed on mount and properly destroyed on unmount.
"use client";
import { useEffect, useState } from "react";
import { runEzoic } from "@/lib/ezoic";
export default function EzoicAd({ id }: { id: number }) {
const [isRendered, setIsRendered] = useState(false);
useEffect(() => {
setIsRendered(true);
runEzoic(() => {
window.ezstandalone?.showAds(id);
});
return () => {
runEzoic(() => {
window.ezstandalone?.destroyPlaceholders(id);
});
};
}, [id]);
return (
<div
className="ezoic-ad-container"
>
{isRendered && <div id={`ezoic-pub-ad-placeholder-${id}`} />}
</div>
);
}
Handling SPA Navigation (App Router) π
Create components/EzoicRouteHandler.tsx. Since the page doesn't refresh on route changes, we use usePathname to trigger a re-scan of the page for new ads.
"use client";
import { useEffect } from "react";
import { usePathname } from "next/navigation";
import { runEzoic } from "@/lib/ezoic";
export default function EzoicRouteHandler() {
const pathname = usePathname();
useEffect(() => {
runEzoic(() => {
requestAnimationFrame(() => {
window.ezstandalone?.showAds();
});
});
}, [pathname]);
return null;
}
Pages Router Implementation π
For the Pages Router, handle the route events inside _app.tsx.
import { useEffect } from "react";
import { useRouter } from "next/router";
import { runEzoic } from "@/lib/ezoic";
export default function App({ Component, pageProps }) {
const router = useRouter();
useEffect(() => {
const handleRoute = () => {
runEzoic(() => {
window.ezstandalone?.showAds();
});
};
router.events.on("routeChangeComplete", handleRoute);
return () => router.events.off("routeChangeComplete", handleRoute);
}, [router.events]);
return <Component {...pageProps} />;
}