Skip to main content

Installation on React and Next.js

This guide is for sites built with React — a single-page application (for example with Vite or Create React App) or Next.js — rather than on a website builder. Using Vue, Angular or Svelte? See the Vue, Angular and Svelte guides. It covers two ways to add the XAPP Chat Widget:

  • Floating chat — the standard chat button and window, on every page. Uses the script snippet.
  • Embedded chat — the chat window placed inside your own page layout, for example on a "Talk to us" page. Uses the @xapp/chat-widget React component.

The widget supports React 18 and React 19, and both methods work with client-side routing such as React Router.

Prerequisites​

  • Access to your application's source code and a developer who can build and deploy it
  • Your chat widget key from Studio
    • See instructions here

Floating Chat (Script Snippet)​

  1. Open your app's HTML entry file

    This is the single HTML file your app is served from. For Vite it is index.html in the project root; for Create React App it is public/index.html.

  2. Add the snippet just before the closing </body> tag

    <script
    id="xapp-js"
    src="https://widget.xapp.ai/xapp-chat-widget.js?key=YOUR_CHAT_KEY"
    ></script>

    Note! Please replace "YOUR_CHAT_KEY" with your actual widget key.

  3. Build and deploy your app

    The snippet goes in the source HTML file, so it is included in the next build. Editing the built output directly will be overwritten by the next deploy.

The widget loads once and stays on screen as visitors move between pages — client-side route changes do not reload it. All appearance and behavior settings come from Studio, so there is nothing else to configure in code.

Embedded Chat (React Component)​

Use this when you want the chat to sit inside your own layout instead of floating over the page.

tip

Try it first on the Embedded Chat Examples page — live layouts with source code you can copy, and a field to preview your own widget key.

  1. Install the packages

    npm install @xapp/chat-widget react-redux stentor-models

    (or yarn add / pnpm add with the same packages). react, react-dom, react-redux and stentor-models are peer dependencies; React 18 and 19 are both supported.

  2. Add a component that loads your Studio configuration

    The component fetches the same configuration the script snippet uses, so changes made in Studio apply here too.

    import { useEffect, useState } from "react";
    import { Chat as XappChat, WidgetEnv } from "@xapp/chat-widget";
    import "@xapp/chat-widget/dist/index.css";

    const CHAT_KEY = "YOUR_CHAT_KEY";

    async function loadChatConfig(): Promise<WidgetEnv> {
    const res = await fetch(`https://widget.xapp.ai/config.json?key=${CHAT_KEY}`);
    if (!res.ok) {
    throw new Error(`Chat config request failed: ${res.status}`);
    }
    return res.json();
    }

    export function EmbeddedChat() {
    const [config, setConfig] = useState<WidgetEnv | null>(null);

    useEffect(() => {
    let cancelled = false;
    loadChatConfig()
    .then((studioConfig) => {
    if (!cancelled) {
    setConfig({
    ...studioConfig,
    // The action bar is a floating page element; hide it inside an embedded chat.
    actionBar: studioConfig.actionBar && { ...studioConfig.actionBar, enabled: false },
    });
    }
    })
    .catch((error) => console.error(error));
    return () => {
    cancelled = true;
    };
    }, []);

    if (!config) {
    return null;
    }
    return <XappChat config={config} mode="docked" />;
    }

    Note! Please replace "YOUR_CHAT_KEY" with your actual widget key.

    For plain JavaScript, remove the type annotations (: Promise<WidgetEnv>, <WidgetEnv | null>) and the WidgetEnv import.

  3. Place it in a container with a height

    In docked mode the chat fills the element it is placed in, so give that element a height (and a width if you want it narrower than the column):

    <div style={{ height: 600, maxWidth: 420 }}>
    <EmbeddedChat />
    </div>
note

If your app already imports a component named Chat or ChatWidget, import ours under a different name, as shown above (Chat as XappChat).

Display Modes​

modeWhat it looks like
dockedFills its container and is always open. Use this for embedding.
normalThe standard floating chat button and window, same as the script snippet.
staticAn always-open chat window fixed to the corner of the screen. It does not sit inside your layout.

Next.js (App Router)​

The same component works in Next.js with two changes, both needed for next build to succeed:

  • Load it only in the browser. The widget reads browser storage as soon as it loads, which fails while Next.js pre-renders the page on the server. Wrap it with next/dynamic and ssr: false.
  • Load the stylesheet from the XAPP CDN. Next.js 16's default bundler (Turbopack) cannot parse the stylesheet shipped in the npm package, so link https://widget.xapp.ai/xapp-chat-widget.css instead of importing @xapp/chat-widget/dist/index.css.
  1. Install the packages

    npm install @xapp/chat-widget react-redux stentor-models
  2. Add the chat component — components/EmbeddedChat.tsx

    "use client";

    import { useEffect, useState } from "react";
    import { Chat as XappChat, WidgetEnv } from "@xapp/chat-widget";

    const CHAT_KEY = "YOUR_CHAT_KEY";

    async function loadChatConfig(): Promise<WidgetEnv> {
    const res = await fetch(`https://widget.xapp.ai/config.json?key=${CHAT_KEY}`);
    if (!res.ok) {
    throw new Error(`Chat config request failed: ${res.status}`);
    }
    return res.json();
    }

    export function EmbeddedChat() {
    const [config, setConfig] = useState<WidgetEnv | null>(null);

    useEffect(() => {
    let cancelled = false;
    loadChatConfig()
    .then((studioConfig) => {
    if (!cancelled) {
    setConfig({
    ...studioConfig,
    actionBar: studioConfig.actionBar && { ...studioConfig.actionBar, enabled: false },
    });
    }
    })
    .catch((error) => console.error(error));
    return () => {
    cancelled = true;
    };
    }, []);

    if (!config) {
    return null;
    }
    return (
    <>
    <link rel="stylesheet" href="https://widget.xapp.ai/xapp-chat-widget.css" precedence="default" />
    <XappChat config={config} mode="docked" />
    </>
    );
    }

    Note! Please replace "YOUR_CHAT_KEY" with your actual widget key.

  3. Add a browser-only wrapper — components/ChatEmbed.tsx

    "use client";

    import dynamic from "next/dynamic";

    // The widget reads browser storage when it loads, so it must not run during server rendering.
    export const ChatEmbed = dynamic(() => import("./EmbeddedChat").then((m) => m.EmbeddedChat), { ssr: false });
  4. Use the wrapper in a page — for example app/help/page.tsx

    import { ChatEmbed } from "@/components/ChatEmbed";

    export default function HelpPage() {
    return (
    <main>
    <h1>Talk to us</h1>
    <div style={{ height: 600, maxWidth: 420 }}>
    <ChatEmbed />
    </div>
    </main>
    );
    }

Hide the Chat Header (Optional)​

When your page already has its own heading, the chat's title bar repeats it. Add header to the config in EmbeddedChat — the same change works for React and Next.js:

setConfig({
...studioConfig,
actionBar: studioConfig.actionBar && { ...studioConfig.actionBar, enabled: false },
header: { ...studioConfig.header, hidden: true },
});
  • Requires @xapp/chat-widget 1.103.0 or later.
  • Applies in docked and static mode only. A floating (normal) chat always keeps its header, because that is where its minimize and close buttons are.
  • If your menu button is set to appear in the header, it moves to the footer so the menu stays reachable.

Try it with the Hide header option on the Embedded Chat Examples page.

Follow the Visitor as They Scroll (Optional)​

An embedded chat scrolls away with the page, so a visitor who carries on reading loses the conversation they started. popOut lets it follow them: while its place on the page is off-screen the chat moves into a floating window in the corner, and it returns when they scroll back to it.

const config: WidgetEnv = {
...studioConfig,
popOut: { enabled: true },
};

It is the same chat that moves, not a second one, so the conversation carries on — including a message being typed. On a phone it offers the chat button (or the action bar) instead of a window covering the screen; tapping that opens the conversation where they left it.

OptionDefaultWhat it does
enabledfalseTurns it on. Only for an embedded (docked) chat — a floating chat already floats.
after"seen""seen" follows anyone who scrolls past. "message" waits until the visitor has sent something, so a chat nobody has spoken to stays on the page. The assistant's greeting does not count.
minWidth640Narrowest window that opens the floating chat. Below this, see mobile.
mobile"auto"What a narrow screen gets: "auto" (the action bar if the app has one, otherwise the chat button), "actionBar", "button", or "none".
  • Requires @xapp/chat-widget 1.104.0 or later.
  • The chat must have been on screen once before it follows anyone, so a chat below the fold never floats in front of a visitor who has not reached it yet.
  • The floating window always has a header, even with header.hidden set, because that is how the visitor puts it away. Closing it returns the chat to the page — on a phone, back to the entry point — and it follows again the next time they scroll past.
  • Where the visitor is reading is kept across the move: at the newest message they stay there, and reading back through the thread keeps their place.

Using Both​

Do not combine the script snippet with an embedded chat on the same page — visitors would see two chat windows.

If you want floating chat across the site and an embedded chat on one page, use the React component for both: render it with mode="normal" in your app layout, and skip it on the page that shows the embedded chat.

import { useLocation } from "react-router-dom";

function Layout() {
const { pathname } = useLocation();
return (
<>
{/* ...your routes... */}
{pathname !== "/chat" && <FloatingChat />}
</>
);
}

Here FloatingChat is the same component as EmbeddedChat above, using the Studio config unchanged (keep the action bar) and mode="normal".

Verify Installation​

  1. Load your site and confirm the chat appears and shows its welcome message.
  2. Navigate between a few pages without refreshing — the floating chat should stay on screen, and an embedded chat should appear only where you placed it.
  3. Check the browser console for errors mentioning the chat widget.

Troubleshooting​

  • Chat opens automatically on page load: This is the Auto Open Chat Widget setting in your chat settings in Studio, not something set in code.
  • Embedded chat is not visible: Its container has no height. Give the container a fixed or flex height.
  • Two chat windows on one page: The script snippet and the React component are both installed. See Using Both.