Add the Chat to Your Page#

This page covers the second step of embedding Neo chat: adding the chat to your page, checking it, and matching it to your site. It assumes a running proxy, as described on the Set Up the Proxy page.

With a Script Tag#

For a page without a build step, the SDK is a single JavaScript file, neo-sdk.js. To host it yourself, copy dist/embed/neo-sdk.js and dist/embed/neo-sdk.js.LICENSE.txt from the package on the proxy server to one folder of your site, together with THIRD-PARTY-NOTICES.md from the root of the package. The licenses of the bundled packages require both license files to go wherever the script goes.

Alternatively, load the script from a public npm CDN, such as jsDelivr, with an exact version and the integrity hash of the file, so that the CDN can never serve your readers a different file. Set crossorigin="anonymous" on the tag, without which the browser blocks the script. The docs/script-tag.md file in the package shows the tag.

Then add the script and the element to your page:

<script src="/assets/neo-sdk.js" defer></script>

<neo-chat base-url="/api/chat" project-id="YOUR_PROJECT_ID" auth="anonymous"></neo-chat>

neo-sdk.js is about 689 kB gzipped and includes the search and document widgets. With defer, it does not block your page.

base-url is the address of your proxy plus its mount path, never your Squirro instance. For a proxy on another origin, give the full URL, such as https://chat-proxy.example.com/api/chat. auth="anonymous" tells the chat to fetch its tokens from the proxy.

An inline <neo-chat> needs a height:

neo-chat {
  display: block;
  height: 600px;
}

Besides base-url, project-id, and auth, the element takes attributes for its layout, theme, page context, links, and welcome. For every attribute, see the Element Attributes section of the Configuration Reference page.

Set anything that markup cannot express, such as functions and nested objects, as a property of the element. The element reads its configuration once, when it starts, and ignores later changes. A tag in your HTML starts once the HTML has loaded, so a script right after the tag is in time, whether neo-sdk.js loads with defer or not:

<script src="/assets/neo-sdk.js" defer></script>
<neo-chat id="chat" base-url="/api/chat" project-id="YOUR_PROJECT_ID" auth="token"></neo-chat>
<script>
  const chat = document.getElementById('chat');
  chat.getToken = () => fetch('/my-session-token').then((response) => response.text());
  chat.theme = { primary: '#0f766e', mode: 'auto', dark: { primary: '#2dd4bf' } };
</script>

An element that you create from a script starts once you add it to the page, so set its properties before you add it.

If your framework manages the DOM itself, use NeoSdk.mount on an element that you already have. It takes the same options as the React setup in the next section, and returns { sdk, unmount }:

const { sdk, unmount } = NeoSdk.mount(document.getElementById('chat-host'), {
  baseUrl: '/api/chat',
  projectId: 'YOUR_PROJECT_ID',
  auth: 'anonymous',
});

The chat renders inside a shadow root. The CSS of your page cannot restyle it by accident, and the styles of the chat cannot leak onto your page.

With React#

Install the package, import its stylesheet once, and wrap the chat in its provider:

import '@squirro/neo-sdk/styles.css';
import { initNeoSdk, NeoChat, NeoSdkProvider } from '@squirro/neo-sdk';

// Once, outside render.
const sdk = initNeoSdk({
  baseUrl: '/api/chat',
  projectId: 'YOUR_PROJECT_ID',
  auth: 'anonymous',
});

export function HelpPanel() {
  return (
    <NeoSdkProvider sdk={sdk}>
      <NeoChat className="h-full" />
    </NeoSdkProvider>
  );
}

To install the package, run npm install @squirro/neo-sdk@~0.1 in the folder of your React app. The components need react and react-dom 19.3.0 or later, and @tanstack/react-query 5.103.2 or later. npm also installs the other libraries that the core expects, such as react-router-dom 7, zustand 5, and lucide-react 0.525, and reports a conflict if your app uses a version outside the range that the core accepts. The docs/installing.md file in the package covers the TypeScript settings and the development server setup. The stylesheet is scoped to the chat: it does not reset or restyle anything else on your page.

Signing Readers In#

auth says how the page gets the token that it sends to the proxy:

auth

The page

Pair it with

anonymous

Fetches a token from the /token route of the proxy.

anonymous(...) on the proxy.

token

Calls your getToken() and sends what it returns.

bearerSession(verify) on the proxy.

none

Sends no token.

A proxy that checks something else, such as a same-origin session cookie or a network allowlist.

For the values that getToken can return, see the Options section of the Configuration Reference page.

A session cookie reaches the proxy only when the proxy is on the same origin as your page. For a proxy on another origin, use token or anonymous.

Check the Chat#

Open your page, and ask a question that your documents answer. The answer appears as it is written, with citations to the documents. Open a cited PDF document. With NUTRIENT_LICENSE_KEY set, it shows without an evaluation watermark. If the chat shows an error instead, see the Troubleshooting page.

Match Your Site#

Colors and Fonts#

With no theme, the chat uses a neutral gray palette in light mode, and the font of your page. Set theme to match your brand:

theme: {
  primary: '#0f766e',
  radius: '6px',
  mode: 'auto',
  dark: { primary: '#2dd4bf', background: '#111827' },
}

For the color keys of the theme, see the Theme Keys section of the Configuration Reference page. The theme also takes the following options:

  • mode

    light, the default, dark, or auto, which follows the system setting of the reader.

  • dark

    The same color keys, for dark mode only. A brand primary chosen for a light page is usually too dark on a dark one, so set a lighter dark.primary, with a contrast ratio of 4.5:1 or more against dark.background.

  • fontFamily and fontSize

    Override the font inherited from your page. The chat never loads a font itself.

  • preset

    squirro uses the Squirro palette instead of the neutral one.

On a script-tag page, you can also write the theme as JSON inside the element:

<neo-chat base-url="/api/chat" project-id="YOUR_PROJECT_ID" auth="anonymous">
  <script type="application/json">
    { "primary": "#0f766e", "radius": "6px", "mode": "auto", "dark": { "primary": "#2dd4bf" } }
  </script>
</neo-chat>

Inline or Floating#

Inline is the default. The chat fills the box that you give it, like any other part of your page.

Floating adds a launcher button in a corner of the window, which opens the chat as a panel above your page:

layout: {
  mode: 'floating',
  position: 'bottom-right', // or 'bottom-left'
  launcherLabel: 'Ask the docs',
  panelSubtitle: 'Answers from our documentation', // optional line under the title
  panelWidth: '420px',
  panelHeight: '70vh',
  defaultOpen: false, // true shows the panel open when the page loads
}

Open and close the panel from your own code with sdk.open() and sdk.close(). For where sdk comes from, see the Ask from Your Own Buttons section of the Connecting the Chat to Your Page page.

Welcome the Reader#

Until the first question, the chat shows a welcome: a heading, a line under it, and any questions that you suggest, which the reader sends with one click. Set welcome to change it:

welcome: {
  title: 'Ask the Northwind docs',
  message: 'Answers link to the docs pages.',
  suggestions: ['Where do I set up webhooks?', 'How do I authenticate?'],
}

The chat shows up to four suggestions and sends a clicked one as if the reader had typed it.

On a script-tag page, set the welcome-title, welcome-message, and welcome-suggestions attributes instead, with the questions separated by |.

In React, <NeoChat welcome={{ ... }} /> sets the welcome for one chat, and takes precedence over welcome in initNeoSdk(). welcomeContent replaces the welcome with your own content instead.