Next.js Integration
This guide covers the recommended ways to integrate the Intufind chat widget into a Next.js application.
Quick Start (Script Tag)
The simplest approach—add a single script tag to your layout:
Option 1: Data Attributes (Recommended)
Add to your root layout (app/layout.tsx):
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
{/* Intufind Chat Widget */}
<script
src="https://cdn.intufind.com/widget/v2/loader.js"
data-publishable-key="if_pk_your_key_here"
async
/>
</body>
</html>
);
}
That's it! The widget will appear on all pages.
Option 2: Config Object
For more configuration options, use the config object approach:
import Script from 'next/script';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
{/* Widget Configuration */}
<Script id="intufind-config" strategy="beforeInteractive">
{`
window.intufindConfig = {
publishableKey: '${process.env.NEXT_PUBLIC_INTUFIND_PUBLISHABLE_KEY}',
title: 'AI Assistant',
greeting: 'How can I help you today?',
widgetPosition: 'bottom-right',
};
`}
</Script>
{/* Widget Loader */}
<Script
src="https://cdn.intufind.com/widget/v2/loader.js"
strategy="afterInteractive"
/>
</body>
</html>
);
}
React Component Approach
For more control, create a dedicated component:
// components/ChatWidget.tsx
'use client';
import { useEffect } from 'react';
// Extend Window interface for TypeScript
declare global {
interface Window {
intufindConfig?: Record<string, unknown>;
IntufindChatbot?: {
open: () => void;
close: () => void;
toggle: () => void;
isOpen: () => boolean;
configure: (config: Record<string, unknown>) => boolean;
setTheme: (theme: Record<string, unknown>) => boolean;
};
}
}
interface ChatWidgetProps {
publishableKey: string;
title?: string;
greeting?: string;
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
}
export function ChatWidget({
publishableKey,
title = 'AI Assistant',
greeting,
position = 'bottom-right',
}: ChatWidgetProps) {
useEffect(() => {
if (window.IntufindChatbot) return;
window.intufindConfig = {
publishableKey,
title,
greeting,
widgetPosition: position,
};
const script = document.createElement('script');
script.src = 'https://cdn.intufind.com/widget/v2/loader.js';
script.async = true;
document.body.appendChild(script);
return () => {
window.IntufindChatbot?.destroy?.();
};
}, [publishableKey, title, greeting, position]);
return null;
}
Use it in your layout:
// app/layout.tsx
import { ChatWidget } from '@/components/ChatWidget';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<ChatWidget
publishableKey={process.env.NEXT_PUBLIC_INTUFIND_PUBLISHABLE_KEY!}
title="Support Bot"
greeting="Hi! How can I help?"
/>
</body>
</html>
);
}
Environment Variables
Add to your .env.local:
NEXT_PUBLIC_INTUFIND_PUBLISHABLE_KEY=if_pk_your_key_here
Get your publishable key from the Intufind Dashboard.
Programmatic Control
Control the widget from anywhere in your app:
'use client';
export function ChatButton() {
const openChat = () => {
window.IntufindChatbot?.open();
};
return (
<button onClick={openChat}>
Chat with us
</button>
);
}
Custom Hook
Create a reusable hook:
// hooks/useChatbot.ts
'use client';
export function useChatbot() {
return {
open: () => window.IntufindChatbot?.open(),
close: () => window.IntufindChatbot?.close(),
toggle: () => window.IntufindChatbot?.toggle(),
isOpen: () => window.IntufindChatbot?.isOpen() ?? false,
configure: (config: Record<string, unknown>) =>
window.IntufindChatbot?.configure(config) ?? false,
setTheme: (theme: Record<string, unknown>) =>
window.IntufindChatbot?.setTheme(theme) ?? false,
};
}
Conditional Loading
Load the widget only on certain pages:
// app/(marketing)/layout.tsx
import { ChatWidget } from '@/components/ChatWidget';
export default function MarketingLayout({ children }: { children: React.ReactNode }) {
return (
<>
{children}
{/* Widget only on marketing pages */}
<ChatWidget publishableKey={process.env.NEXT_PUBLIC_INTUFIND_PUBLISHABLE_KEY!} />
</>
);
}
Or exclude from specific pages:
'use client';
import { usePathname } from 'next/navigation';
import { ChatWidget } from '@/components/ChatWidget';
export function ConditionalChatWidget() {
const pathname = usePathname();
// Don't show on checkout or admin pages
const excludedPaths = ['/checkout', '/admin'];
const shouldShow = !excludedPaths.some(path => pathname.startsWith(path));
if (!shouldShow) return null;
return <ChatWidget publishableKey={process.env.NEXT_PUBLIC_INTUFIND_PUBLISHABLE_KEY!} />;
}
Theme Integration
Match the widget to your site's theme:
<ChatWidget
publishableKey={process.env.NEXT_PUBLIC_INTUFIND_PUBLISHABLE_KEY!}
config={{
theme: {
colors: {
primaryColor: '#6366f1', // Indigo
widgetBackground: '#ffffff',
userBubbleBackground: '#6366f1',
},
typography: {
fontFamily: 'var(--font-inter)', // Use your CSS variable
},
},
}}
/>
Dark Mode Support
'use client';
import { useTheme } from 'next-themes';
import { useEffect } from 'react';
export function ChatWidgetWithTheme() {
const { theme } = useTheme();
useEffect(() => {
if (!window.IntufindChatbot) return;
const isDark = theme === 'dark';
window.IntufindChatbot.setTheme({
colors: {
widgetBackground: isDark ? '#1a1a1d' : '#ffffff',
assistantBubbleBackground: isDark ? '#252528' : '#f3f4f6',
inputTextColor: isDark ? '#fafafa' : '#1f2937',
},
});
}, [theme]);
return null;
}
TypeScript Types
For full TypeScript support, add these types:
// types/intufind.d.ts
declare global {
interface Window {
intufindConfig?: IntufindConfig;
IntufindChatbot?: IntufindChatbotAPI;
}
}
interface IntufindConfig {
publishableKey: string;
apiUrl?: string;
widgetUrl?: string;
widgetPosition?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
title?: string;
subtitle?: string;
greeting?: string;
avatar?: string;
triggerAvatar?: string;
triggerShowBackground?: boolean;
triggerIconShowBorder?: boolean;
showBranding?: boolean;
debugMode?: boolean;
theme?: Record<string, unknown>;
prompts?: Array<{ text: string; icon?: string }>;
}
interface IntufindChatbotAPI {
open: () => void;
close: () => void;
toggle: () => void;
isOpen: () => boolean;
getState: () => { isOpen: boolean; config: IntufindConfig };
getConfig: () => IntufindConfig;
configure: (config: Partial<IntufindConfig>) => boolean;
setTheme: (theme: Record<string, unknown>) => boolean;
setCustomCSS: (css: string) => boolean;
version: string;
isInitialized: () => boolean;
destroy: () => void;
}
export {};
Troubleshooting
Widget not appearing
- Check the console for errors
- Verify your publishable key starts with
if_pk_ - Ensure the script loads after hydration (use
strategy="afterInteractive")
Hydration mismatch errors
The widget runs client-side only. Use the 'use client' directive and useEffect:
'use client';
import { useEffect, useState } from 'react';
export function ChatWidget() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) return null;
// ... rest of component
}
Multiple widget instances
Ensure you only render the widget component once, typically in your root layout.
Next Steps
- Widget Customization — Full configuration reference
- Widget Styling — CSS customization
- Chat Prompts — Configure suggested prompts