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.
App Router
The stylesheet in the head, the container in the body, and the SDK on afterInteractive.
// 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.
// 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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
