Skip to content
Documentation

Install the chat widget in Next.js and React

Add the intoCHAT widget to a Next.js app with next/script in the root layout, or to a React app with index.html or a useEffect hook, without duplicate bubbles.

The intoCHAT widget is a plain script, not a React component. It reads your agent ID from its data-chatbot-id attribute and adds the launcher to document.body, outside your React tree, so re-renders and route changes don't touch it.

Next.js App Router

Load the script once in the root layout with the Script component from next/script:

import Script from 'next/script'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://www.intochat.ai/api/embed.js"
          data-chatbot-id="YOUR_AGENT_ID"
          strategy="lazyOnload"
        />
      </body>
    </html>
  )
}
  • The data attribute. Next.js forwards extra attributes such as data-chatbot-id to the final script element, which is where the widget looks for it.
  • The strategy. lazyOnload loads the widget during browser idle time, after the page's other resources. afterInteractive, the default, loads it earlier, so the launcher appears sooner. Both work.
  • Not beforeInteractive. That strategy is for scripts the page needs before it becomes interactive. The chat widget isn't one, and loading it that early only competes with your own code.
  • Server Components. The layout can stay a Server Component, because this Script has no event handlers.

Next.js loads the script only once, even as visitors move between routes. In the Pages Router, add the same Script to pages/_app instead.

To show the chat under some routes only, put the Script in a nested layout. Once loaded, the launcher stays on the page after client-side navigation to other routes, until the next full page load.

React with Vite or Create React App

Choose one of two options, not both. The embed script ignores a second copy for the same agent, so using both only loads it twice.

Option 1: index.html. Paste the script tag from the Share tab just before </body> in index.html: in the project root for Vite, or in public/index.html for Create React App.

Option 2: a component. Use this if you want to decide in code whether the widget loads, for example only in production:

import { useEffect } from 'react'

export default function IntoChatWidget() {
  useEffect(() => {
    // Load the widget only once, even when StrictMode runs effects twice
    if (document.getElementById('intochat-embed')) return

    const script = document.createElement('script')
    script.id = 'intochat-embed'
    script.src = 'https://www.intochat.ai/api/embed.js'
    script.setAttribute('data-chatbot-id', 'YOUR_AGENT_ID')
    document.body.appendChild(script)
  }, [])

  return null
}

Render IntoChatWidget once, in your root component such as App. Set the attribute before the element is added to the page, as above.

StrictMode and duplicate launchers

In development, React StrictMode runs effects twice. The id check at the top of the effect keeps the script from being added twice. Even without it you wouldn't get a second launcher, because the embed script adds only one per agent and page. Don't remove the script in a cleanup function: removing it doesn't remove the launcher, which stays in document.body until the next full page load.

Client-side actions

The agent can call functions in your app during a chat, for example to read state or open a form. Register them with window.IntoChatActions.register. See Client-side actions.

Content Security Policy

Allow https://www.intochat.ai in script-src, connect-src and frame-src. If your policy uses nonces, pass the nonce to the Script component. Next.js forwards it like the data attribute.

Check it

Open any route. The launcher appears in the bottom corner and stays as you navigate. Two launchers mean two snippets with different agent IDs are loaded. No launcher and "Chatbot not found." in the console means the agent ID is wrong. See Troubleshooting for other cases, and the Next.js and React guides for more background.

View as Markdown