Embed the Maison concierge chat widget in a Next.js application. Both the App Router and Pages Router are supported. Next.js official site ↗
window does not exist on the server. Always access it inside useEffect (App Router) or in a useEffect in _app.tsx (Pages Router) — both run only in the browser.Prerequisites
- Next.js 13+ (App Router) or Next.js 12+ (Pages Router)
- Your Client UUID — find it in the Business Console under Settings
App Router
Create a MaisonChat client component
The
'use client'directive is required at the top of the file — it marks the component as a client component souseEffectandwindoware accessible.tsx// components/MaisonChat.tsx 'use client'; import { useEffect } from 'react'; export function MaisonChat({ clientId }: { clientId: string }) { useEffect(() => { const script = document.createElement('script'); script.src = 'https://agent.maison-labs.com/agent-inject.bundle.js'; script.setAttribute('data-cfasync', 'false'); script.setAttribute('data-no-minify', '1'); script.setAttribute('data-no-optimize', '1'); script.setAttribute('data-no-defer', '1'); script.setAttribute('data-noptimize', '1'); script.onload = () => { const sessionId = new URLSearchParams(window.location.search).get('mcbsid'); const mcbs = (window as any).initMcbs({ clientId, sessionId, // locale: 'en-US', // Override browser locale // iconPosition: { top: 0, bottom: 20, left: 0, right: 20 }, // Chat icon position (px) // loadChatWindow: 'load', // When the chat window loads: 'load' (default), 'interaction', 'lazy' or 'eager' // onSessionUpdate: (id) => console.log('Session:', id), // onLocaleUpdate: (locale) => console.log('Locale:', locale), }); mcbs.showIcon(); }; document.head.appendChild(script); return() => { document.head.removeChild(script); }; }, [clientId]); return null; }Add MaisonChat to your root layout
Import and render the component inside
app/layout.tsx. Server components can import client components — Next.js handles the boundary automatically.tsx// app/layout.tsx import { MaisonChat } from '@/components/MaisonChat'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return( <html lang="en"> <body> {children} <MaisonChat clientId="YOUR_CLIENT_UUID" /> </body> </html> ); }Verify in DevTools
Open your browser DevTools, switch to the Elements tab, and look for
#maison-chat-iconin the DOM. If it's present, the widget initialised correctly.
Pages Router
Add the widget to pages/_app.tsx
Place the script injection directly in
pages/_app.tsxusinguseEffect. The empty dependency array ensures it runs once after the initial render.tsx// pages/_app.tsx import type { AppProps } from 'next/app'; import { useEffect } from 'react'; export default function App({ Component, pageProps }: AppProps) { useEffect(() => { const script = document.createElement('script'); script.src = 'https://agent.maison-labs.com/agent-inject.bundle.js'; script.setAttribute('data-cfasync', 'false'); script.setAttribute('data-no-minify', '1'); script.setAttribute('data-no-optimize', '1'); script.setAttribute('data-no-defer', '1'); script.setAttribute('data-noptimize', '1'); script.onload = () => { const sessionId = new URLSearchParams(window.location.search).get('mcbsid'); const mcbs = (window as any).initMcbs({ clientId: 'YOUR_CLIENT_UUID', sessionId, // locale: 'en-US', // Override browser locale // iconPosition: { top: 0, bottom: 20, left: 0, right: 20 }, // Chat icon position (px) // loadChatWindow: 'load', // When the chat window loads: 'load' (default), 'interaction', 'lazy' or 'eager' // onSessionUpdate: (id) => console.log('Session:', id), // onLocaleUpdate: (locale) => console.log('Locale:', locale), }); mcbs.showIcon(); }; document.head.appendChild(script); return() => { document.head.removeChild(script); }; }, []); return <Component {...pageProps} />; }Verify in DevTools
Open your browser DevTools, switch to the Elements tab, and look for
#maison-chat-iconin the DOM. If it's present, the widget initialised correctly.
For the full list of configuration options, control methods, and callbacks, see the Widget SDK Reference.
Common mistakes
useEffect or access window. The MaisonChat component file must start with 'use client' as its very first line.window does not exist on the server. Never write const mcbs = window.initMcbs(...) outside a useEffect callback. The entire call must be inside the effect, after the script loads._app.tsx (Pages Router).bottom: 20, not bottom: '20px'. Values are plain numbers representing pixels — do not include units.Not sure if it's set up correctly? Use the Site Diagnostic tool.