Skip to main content
Next.js

Add an AI Chatbot to Your Next.js App

Load the widget with next/script on afterInteractive and mount it from your layout. Working examples for both the App Router and the Pages Router.

Priced in your local currency
No npm package, nothing in your bundle

App Router

The stylesheet in the head, the container in the body, and the SDK on afterInteractive.

tsx
// app/layout.tsx
import Script from "next/script";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <head>
        <link
          rel="stylesheet"
          href="https://cdn.cortexvia.in/chatbotsdk-web.css"
        />
      </head>
      <body>
        {children}

        <div id="cv-chat" />

        {/* afterInteractive: the widget must not compete with hydration. */}
        <Script
          src="https://cdn.cortexvia.in/chatbotsdk-web.iife.js"
          strategy="afterInteractive"
          onLoad={() => {
            window.cvChatHandle = window.ChatbotSDK.mount("#cv-chat", {
              chatbotKey: "ctx-cb-xxxxxxxxxxxxxxxx",
              config: { mode: "chatbot", position: "bottom-right" },
            });
          }}
        />
      </body>
    </html>
  );
}

Because onLoad is a client callback, this file needs to be a client component, or the Script can be lifted into a small client wrapper that layout.tsx renders.

  • layout.tsx renders once and survives navigation
  • afterInteractive keeps the widget out of the hydration path
  • The container div must exist before the mount call runs

Pages Router

The same idea in pages/_app.tsx, which wraps every page in the Pages Router.

tsx
// pages/_app.tsx  (Pages Router)
import Script from "next/script";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Component {...pageProps} />

      <div id="cv-chat" />

      <Script
        src="https://cdn.cortexvia.in/chatbotsdk-web.iife.js"
        strategy="afterInteractive"
        onLoad={() => {
          window.cvChatHandle = window.ChatbotSDK.mount("#cv-chat", {
            chatbotKey: "ctx-cb-xxxxxxxxxxxxxxxx",
            config: { mode: "chatbot" },
          });
        }}
      />
    </>
  );
}

_app.tsx is already a client component, so the onLoad callback needs no extra boundary here.

  • _app.tsx is the Pages Router equivalent of a root layout
  • The stylesheet can go in _document.tsx or a global CSS import
  • Same key, same config shape as the App Router example

Get these right

The mistakes that turn a two-minute integration into an afternoon.

01

afterInteractive, not beforeInteractive

beforeInteractive blocks hydration and is reserved for scripts that must run first. A support widget is the opposite of that, so afterInteractive is correct.

02

Put it in the layout, not a page

app/layout.tsx renders once and survives navigation. Mounting from a page component tears the conversation down on every route change.

03

onLoad needs a client boundary

onLoad is a client callback. In the App Router, either keep the Script in a component marked use client or move it into a small client wrapper.

04

The stylesheet is not a script

next/script only handles JavaScript. The widget stylesheet goes in the head as an ordinary link tag, which is what the layout example does.

Why Cortexvia

A CDN widget with local currency pricing, with the trust pages published up front.

01

No npm dependency

The widget loads from the Cortexvia CDN and attaches to window.ChatbotSDK, so nothing enters your bundle and nothing has to track your Next.js version.

02

Keeps your Core Web Vitals clean

afterInteractive loading means the widget arrives once hydration is done, so it does not push out your LCP or block interaction.

03

Server rendering is unaffected

The mount is a browser-only call, so your server components render exactly as before and there is no hydration mismatch to chase.

04

Local pricing, free tier to test

Plans are priced in your local currency, and the free tier is enough to wire it into a real deployment.

Questions

What developers ask before shipping the widget to production.

More platforms

The same SDK works anywhere a script tag can go. Pick your platform for the exact steps.

01WordPressWPCode, footer.php, or a block-theme footer pattern.
02Shopifytheme.liquid, before the closing body tag.
03WixCustom Code, added to Body end. Needs a published site.
04SquarespaceCode Injection, Footer. Needs a Business plan or higher.
05WebflowSite Settings, Custom Code, Footer Code, then republish.
06ReactMount in an effect, unmount in the cleanup.
07Next.jsnext/script with afterInteractive, App or Pages Router.
08Plain HTMLPaste before the closing body tag. The baseline.

Get started

Start on the free tier with a document or two, add the Script to your layout, and deploy.

Layout, afterInteractive, client boundary. Those three decisions are the whole integration; everything after that is your key and your config.