Docs
Next.js
Next.js App Router should load the snippet once in app/layout.tsx. Prefer a first-party /cashline.js on the measured origin (copy public/cashline.js, or point at your Zarstat deploy). afterInteractive is enough. Do not invent pageviews in getServerSideProps — the browser sends them. localStorage visitor IDs do not cross subdomains.
Markdown · same page for crawlers. index, follow.
Root layout
Use next/script in the root layout. Point src at first-party /cashline.js on this origin, or at your Zarstat host. data-site is the site you already use. zarstat() is an alias for cashline(). The snippet also fires a pageview on SPA href changes.
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en-ZA">
<body>
<Script id="cashline-queue" strategy="beforeInteractive">
{`window.cashline=window.cashline||function(){(window.cashline.q=window.cashline.q||[]).push(arguments)}`}
</Script>
<Script
src="https://YOUR_ORIGIN/cashline.js"
data-site="YOUR_SITE"
strategy="afterInteractive"
onLoad={() => {
const track = window.zarstat || window.cashline;
if (typeof track === "function") track("pageview");
}}
/>
{children}
</body>
</html>
);
}Purchase after the processor confirms
Amount is rand, not cents. Call this from a client handler after Paystack (or your own webhook) says the charge succeeded. Do not fire it from a Server Component.
"use client";
function onPaid(reference: string) {
window.cashline?.("purchase", {
amount: 199,
currency: "ZAR",
reference,
});
}Client-side goals
After a real signup on the client, fire a goal. Do not fire it from a Server Component.
"use client";
function onSignedUp() {
window.cashline?.("goal", { name: "signup" });
}