Next.js logo

Next.js Integration

Embed the Maison concierge chat widget in a Next.js application. Both the App Router and Pages Router are supported. Next.js official site ↗

Never call initMcbs at module level. 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

  1. 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 so useEffect and window are 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.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)
            // 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;
    }
  2. 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>
      );
    }
  3. Verify in DevTools

    Open your browser DevTools, switch to the Elements tab, and look for #maison-chat-icon in the DOM. If it's present, the widget initialised correctly.

Pages Router

  1. Add the widget to pages/_app.tsx

    Place the script injection directly in pages/_app.tsx using useEffect. 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.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)
            // 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} />;
    }
  2. Verify in DevTools

    Open your browser DevTools, switch to the Elements tab, and look for #maison-chat-icon in 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
Missing 'use client' directive (App Router). Server components cannot use useEffect or access window. The MaisonChat component file must start with 'use client' as its very first line.
Calling initMcbs at module level. 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.
Mounting MaisonChat inside a page component. If the component is only rendered on some pages, the widget will unmount and remount during navigation, injecting a new script tag each time. Render it once in the root layout (App Router) or _app.tsx (Pages Router).
Using string values for iconPosition. Write 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.