Implementation with Next.js
This guide demonstrates how to preload the Kameleoon Web Experimentation technology with anti-flicker and execute the script engine.js after React hydration to ensure a seamless user experience.
Overview
- Preload Kameleoon: Download the engine in advance so it’s cached by the time we run it.
- Anti-Flicker CSS: Hide content until the engine is ready, avoiding visible layout shifts.
- Post-Hydration Execution: Insert the engine script after React hydrates, eliminating hydration errors.
Client Configuration
Before adding the code, adjust these Kameleoon settings:
-
Disable SPA mode We’ll manage route changes manually to avoid delays from the built-in URL listener. To disable SPA mode, check this documentation.
-
Enable
enableDynamicRefresh
inglobalScript
Required since SPA mode is disabled. See: Manage your Projects → Global Custom Scipts and Kameleoon Activation API → enableDynamicRefresh
Components
The domain for your Kameleoon scripts may vary from one project to another. Depending on their creation date, your projects may be hosted on either kameleoon.eu
or kameleoon.io
. Ensure you use the domain displayed in your project in the Kameleoon App.
Create two components and insert them at the application level, ensuring they’re the very first tags inside <head>
and <body>
.
1. KameleoonHead
Add this component to <head>
in app/layout.tsx
(or app/_document.tsx
).
import Script from "next/script";
const ANTIFLICKER_CSS = `* { visibility: hidden !important; background-image: none !important; }`;
const ANTIFLICKER_JS = `
var kameleoonLoadingTimeout = 1000;
window.kameleoonQueue = window.kameleoonQueue || [];
window.kameleoonStartLoadTime = new Date().getTime();
window.kameleoonDisplayPage = function (fromEngine) {
if (!fromEngine) {
window.kameleoonTimeout = true;
}
var sheet = document.getElementById("kameleoonLoadingStyleSheet");
if (sheet && sheet.parentNode) {
sheet.parentNode.removeChild(sheet);
}
};
window.kameleoonDisplayPageTimeOut = window.setTimeout(
window.kameleoonDisplayPage,
kameleoonLoadingTimeout
);
`;
export function KameleoonHead() {
return (
<>
{/* Preload Kameleoon engine for instant cached execution */}
<link
rel="preload"
hhref="https://SITECODE.kameleoon.io/engine.js"
as="script"
fetchPriority="high"
/>
{/* Anti-flicker style to prevent hydration issues */}
<style id="kameleoonLoadingStyleSheet">{ANTIFLICKER_CSS}</style>
{/* Anti-flicker logic */}
<Script
id="kameleoon-antiflicker"
strategy="beforeInteractive"
dangerouslySetInnerHTML={{ __html: ANTIFLICKER_JS }}
/>
</>
);
}
Note: The default anti-flicker timeout is
1000 ms
. AdjustkameleoonLoadingTimeout
as needed.
2. KameleoonScript
Place this component inside <body>
. It loads the engine after hydration and handles route changes manually.
"use client";
import { usePathname, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useRef } from "react";
export function KameleoonScript() {
const pathname = usePathname();
const searchParams = useSearchParams();
const isFirstRender = useRef(true);
const loadEngine = useCallback(() => {
if (document.getElementById("kameleoon-engine")) return;
const script = document.createElement("script");
script.src = "https://SITECODE.kameleoon.io/engine.js";
script.async = true;
script.id = "kameleoon-engine";
document.head.appendChild(script);
}, []);
// Execute Kameleoon after hydration
useEffect(() => {
loadEngine();
}, [loadEngine]);
// Manual SPA control for route changes
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
window.Kameleoon?.Analyst?.load();
}, [pathname, searchParams]);
return null;
}
Usage in RootLayout
Example layout combining both components:
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head>
<KameleoonHead />
<link
rel="preload"
as="image"
href="https://images.unsplash.com/photo-1551434678-e076c223a692?ixlib=rb-4.0.3&auto=format&fit=crop&w=2070&q=80"
fetchPriority="high"
/>
</head>
<body>
{/* Client component signals hydration and runs Kameleoon */}
<KameleoonScript />
<div className="min-h-screen bg-background">
<Navigation />
<main className="container mx-auto px-4 py-8">{children}</main>
</div>
</body>
</html>
);
}
Key Takeaways
- Preload Early: Ensures the engine is cached for near-instant execution.
- Hide Then Reveal: Anti-flicker CSS prevents visible page flashes.
- Manual Route Handling: Disabling SPA mode and calling
Analyst.load()
on route changes gives precise control.
With these steps, your Next.js application will load Kameleoon efficiently and without hydration issues.