Skip to main content
This guide walks you through integrating the JigsawStack Translation Widget in a Next.js 13+ project that uses the App Router.
1

Create the widget component

Create a new client component file at components/TranslationWidget.tsx. This component dynamically loads the CDN script and initializes the widget once the document is ready:
"use client";

import { useEffect } from "react";

declare global {
  interface Window {
    TranslationWidget: (
      publicKey: string,
      config?: {
        pageLanguage?: string;
        position?: string;
        autoDetectLanguage?: boolean;
        theme?: {
          baseColor?: string;
          textColor?: string;
        };
        showUI?: boolean;
      }
    ) => void;
  }
}

export default function TranslationWidgetComponent() {
  useEffect(() => {
    const script = document.createElement("script");
    script.src =
      "https://unpkg.com/translation-widget@latest/dist/index.min.js";
    script.defer = true;

    const initWidget = () => {
      if (window.TranslationWidget) {
        window.TranslationWidget("YOUR_PUBLIC_KEY", {
          // configuration options
        });
      }
    };

    script.onload = () => {
      if (document.readyState === "complete") {
        initWidget();
      } else {
        window.addEventListener("load", initWidget);
      }
    };

    document.body.appendChild(script);

    return () => {
      window.removeEventListener("load", initWidget);
      document.body.removeChild(script);
    };
  }, []);

  return null;
}
Replace "YOUR_PUBLIC_KEY" with your public key from the JigsawStack dashboard.The "use client" directive is required because this component uses useEffect and accesses document and window, which are only available in the browser.
2

Add the component to your root layout

Import and render TranslationWidgetComponent inside the <body> of your root layout file at app/layout.tsx:
import TranslationWidgetComponent from "@/components/TranslationWidget";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className="antialiased">
        {children}
        <TranslationWidgetComponent />
      </body>
    </html>
  );
}
Placing the component inside <body> ensures the script is appended to the document body at runtime.

How it works

The component creates a <script> element programmatically and appends it to document.body when the component mounts. Once the script loads, it checks whether the document is already fully loaded:
  • If document.readyState === "complete", it calls initWidget() immediately.
  • Otherwise, it waits for the window load event before initializing.
The useEffect cleanup function removes both the event listener and the script element when the component unmounts, preventing memory leaks.

TypeScript support

The declare global block in the component extends the Window interface so TypeScript recognizes window.TranslationWidget. You can also move this declaration into a dedicated types.d.ts file at the root of your project to share it across your codebase. For more details, see TypeScript Support.

Configuration options

See Configuration Options for the full list of parameters you can pass to window.TranslationWidget.
Looking for the Pages Router guide? See Next.js Pages Router.

Build docs developers (and LLMs) love