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 enableDynamicRefreshinglobalScriptRequired 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. AdjustkameleoonLoadingTimeoutas 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.