Add an AI Chatbot to Your Next.js App (App Router & Pages Router)
How to add an AI chatbot to a Next.js application. Works with App Router and Pages Router. Uses next/script for optimal loading — no useEffect hacks required.
You don't need to build one from scratch
Every developer's first instinct: "I'll just build my own chatbot with the OpenAI API, a vector database, some RAG pipeline, and a custom WebSocket server."
Three weekends later, you've got a partially working prototype, a $47 OpenAI bill, and a deep appreciation for how hard conversational UX actually is.
Or — and hear me out — you could add one line to your layout and move on with your life.
The quick version
If you're using the App Router (Next.js 13+), add this to your root layout:
// app/layout.tsx
import Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://factfu.com/v0/widget.loader.js"
data-widget-key="pk_your_key_here"
strategy="lazyOnload"
/>
</body>
</html>
)
}
If you're on the Pages Router:
// pages/_app.tsx
import Script from 'next/script'
export default function App({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
<Script
src="https://factfu.com/v0/widget.loader.js"
data-widget-key="pk_your_key_here"
strategy="lazyOnload"
/>
</>
)
}
That's it. The widget loads lazily after hydration, renders in a shadow DOM (so it won't fight with your Tailwind classes), and persists across client-side navigations.
But first: get a widget key
You need content for the bot to know about. Here's the setup:
- Head to factfu.com/signup — free tier, 200 messages/month
- Create a bot and name it
- Add content sources:
- URL: give it your production URL and it'll crawl your pages
- Text: paste FAQ content, product specs, policies
- Files: upload docs, PDFs, whatever
- Go to Settings → Widget and copy your widget key (starts with
pk_)
Now use that key in the script tag above.
Why next/script instead of a raw <script> tag
You could technically add a <script> tag to your <Head> component and call it a day. But next/script gives you:
strategy="lazyOnload"— loads the script during idle time, after the page has fully loaded. Zero impact on your Largest Contentful Paint.- Automatic deduplication — if the script tag appears in multiple components, Next.js loads it once.
- Works with SSR and SSG — the script is injected on the client side, so it plays nicely with server rendering.
Using strategy="afterInteractive" also works if you want the widget to appear slightly sooner, but lazyOnload is better for performance.
Does it survive client-side navigation?
Yes. The widget attaches to the DOM once and stays there. When users navigate between pages via next/link, the chat widget maintains its state — conversation history, open/closed state, the works. No re-initialization needed.
This is one advantage of a script-based widget over a React component approach: it's completely decoupled from your React tree and doesn't care about re-renders.
TypeScript? No extra types needed
The script loads externally and renders in its own shadow DOM. There's no TypeScript integration required, no ambient type declarations, no module augmentation. It just works.
If you want to interact with the widget programmatically (open it, close it, send a message), that's a different story — check the Factfu API docs for the widget JavaScript API.
Common questions from Next.js developers
"Won't this hurt my Lighthouse score?"
No. With lazyOnload, the script loads during browser idle time. The widget itself is ~30 KB gzipped. We've tested this extensively — it doesn't affect LCP, FID, or CLS. Your Lighthouse score stays exactly where it was.
"Does it work with Vercel's Edge Runtime?"
The widget is purely client-side. It doesn't care where your app is deployed — Vercel, Netlify, Cloudflare, a Raspberry Pi in your closet. If there's a browser loading your page, the widget works.
"What about ISR / static pages?"
Works perfectly. The script tag is always in your layout, and the widget initializes on the client regardless of how the page was rendered.
"Can I conditionally show the widget on certain pages?"
Yes — you can conditionally render the Script component:
// Only show on marketing pages, not the dashboard
const pathname = usePathname()
const showWidget = !pathname.startsWith('/dashboard')
return showWidget ? (
<Script
src="https://factfu.com/v0/widget.loader.js"
data-widget-key="pk_your_key_here"
strategy="lazyOnload"
/>
) : null
"I'm using the app directory with Server Components. Is the Script okay in a Server Component?"
Yes. next/script works in Server Components. It injects the script tag into the rendered HTML and hydrates it on the client. No 'use client' directive needed just for the Script component.
Troubleshooting
Widget doesn't appear in development:
- Run
next devand check your browser console for errors - Make sure the widget key is correct (starts with
pk_) - The widget won't appear in server-side renders — it only loads in the browser
Widget vanishes after a navigation:
- This shouldn't happen with
next/script. If you're manually adding a<script>tag inside a component that unmounts, switch to thenext/scriptapproach above.
Widget conflicts with my styling:
- The widget uses shadow DOM, so your global CSS won't affect it. If something looks off, it's likely a z-index issue — the widget uses
z-index: 2147483647(max safe value).
Pricing
| Free | Starter ($19/mo) | Pro ($49/mo) | |
|---|---|---|---|
| Bots | 1 | 3 | Unlimited |
| Messages/month | 200 | 2,000 | 5,000 |
| Content sources | 5 | 25 | Unlimited |
| Custom branding | ✓ | ✓ | ✓ |
| Live chat handoff | — | ✓ | ✓ |
| Zapier integration | — | ✓ | ✓ |
| Slack integration | — | — | ✓ |
| Remove "Powered by" | — | — | ✓ |
Wrapping up
You don't need to build a chatbot from scratch. You don't need a vector database. You don't need to fine-tune a model. You need one next/script tag in your layout and five minutes of content setup.
Your users get instant answers. You get to ship the feature you were actually supposed to be working on this sprint.
Ready to add AI chat to your site?
Set up Factfu in under 5 minutes. No credit card required.
Start for free →