title: React & Next.js description: Integrate the Navigic widget into React and Next.js applications. order: 8

React & Next.js

The Navigic widget works in any React or Next.js app. This guide shows how to load the script, initialize the widget, and handle identity in a React-friendly way.

Basic Setup

Create a component that loads the embed script and initializes the widget:

"use client"; // Next.js App Router
 
import { useEffect } from "react";
 
const EMBED_SCRIPT_URL = "https://releases.navigic.ai/embed/embed.js?v=7";
const WORKSPACE_ID = "pk_wk_YOUR_KEY";
 
export function NavigicChat() {
  useEffect(() => {
    // Don't load twice
    if (document.querySelector(`script[src^="${EMBED_SCRIPT_URL}"]`)) {
      return;
    }
 
    const script = document.createElement("script");
    script.src = EMBED_SCRIPT_URL;
    script.async = true;
    script.dataset.apiKey = WORKSPACE_ID;
    script.dataset.manual = "true";
 
    script.onload = () => {
      window.NavigicWidget?.init({
        workspaceId: WORKSPACE_ID,
        displayMode: "bubble",
        position: "bottom-right",
      });
    };
 
    document.body.appendChild(script);
 
    return () => {
      window.NavigicWidget?.destroy();
    };
  }, []);
 
  return null;
}

Then render it in your layout:

// app/layout.tsx (Next.js App Router)
import { NavigicChat } from "@/components/navigic-chat";
 
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <NavigicChat />
      </body>
    </html>
  );
}

TypeScript

Add a type declaration for the NavigicWidget global:

// types/navigic.d.ts
interface NavigicWidgetAPI {
  init(config: {
    workspaceId: string;
    displayMode?: "bubble" | "panel" | "inline";
    position?: "bottom-right" | "bottom-left";
    primaryColor?: string;
    title?: string;
    greeting?: string;
    gatewayUrl?: string;
    panelWidth?: string;
    panelSide?: "left" | "right";
    targetElement?: string | HTMLElement;
    mode?: "live" | "mock";
  }): void;
  destroy(): void;
  open(): void;
  close(): void;
  identify(
    userId: string,
    traits?: Record<string, string>,
    auth?: { userHash?: string },
  ): void;
  reset(): void;
}
 
declare global {
  interface Window {
    NavigicWidget?: NavigicWidgetAPI;
  }
}
 
export {};

Identity with Auth Providers

If you use an auth provider (Clerk, NextAuth, Auth0, etc.), identify the user after they sign in.

With Clerk

"use client";
 
import { useEffect } from "react";
import { useUser } from "@clerk/nextjs";
 
export function NavigicIdentify() {
  const { user, isSignedIn } = useUser();
 
  useEffect(() => {
    if (!isSignedIn || !user) return;
 
    // Fetch HMAC from your API route
    fetch("/api/embed-identity", { method: "POST" })
      .then((res) => res.json())
      .then(({ userHash }) => {
        window.NavigicWidget?.identify(
          user.id,
          {
            email: user.primaryEmailAddress?.emailAddress ?? "",
            name: user.fullName ?? "",
          },
          { userHash },
        );
      });
  }, [isSignedIn, user]);
 
  return null;
}

With NextAuth

"use client";
 
import { useEffect } from "react";
import { useSession } from "next-auth/react";
 
export function NavigicIdentify() {
  const { data: session } = useSession();
 
  useEffect(() => {
    if (!session?.user) return;
 
    fetch("/api/embed-identity", { method: "POST" })
      .then((res) => res.json())
      .then(({ userHash }) => {
        window.NavigicWidget?.identify(
          session.user.id,
          {
            email: session.user.email ?? "",
            name: session.user.name ?? "",
          },
          { userHash },
        );
      });
  }, [session]);
 
  return null;
}

Reset on Logout

Call reset() in your sign-out handler:

function handleSignOut() {
  window.NavigicWidget?.reset();
  // ... your sign-out logic
}

Inline Mode in React

For inline mode, pass a ref to the container element:

"use client";
 
import { useEffect, useRef } from "react";
 
export function NavigicInline() {
  const containerRef = useRef<HTMLDivElement>(null);
 
  useEffect(() => {
    if (!containerRef.current) return;
 
    const script = document.createElement("script");
    script.src = "https://releases.navigic.ai/embed/embed.js?v=7";
    script.async = true;
    script.dataset.apiKey = "pk_wk_YOUR_KEY";
    script.dataset.manual = "true";
 
    script.onload = () => {
      window.NavigicWidget?.init({
        workspaceId: "pk_wk_YOUR_KEY",
        displayMode: "inline",
        targetElement: containerRef.current!,
      });
    };
 
    document.body.appendChild(script);
 
    return () => {
      window.NavigicWidget?.destroy();
    };
  }, []);
 
  return <div ref={containerRef} style={{ height: 600, width: "100%" }} />;
}

Next.js Pages Router

If you're using the Pages Router instead of App Router, the setup is the same — just remove the "use client" directive and use _app.tsx:

// pages/_app.tsx
import type { AppProps } from "next/app";
import { NavigicChat } from "@/components/navigic-chat";
 
export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Component {...pageProps} />
      <NavigicChat />
    </>
  );
}