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";
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",
showToolCalls: true,
showReasoning: false,
});
};
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";
showToolCalls?: boolean;
showReasoning?: boolean;
}): 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 {};Omit showToolCalls and showReasoning when the page should inherit the
dashboard embed-key policy. Set them only when this specific install should make
an explicit visibility request.
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";
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 />
</>
);
}