---
title: "Integrate n8n Chat Widget into Next.js (Production-Ready)"
slug: "integrate-n8n-chat-widget-nextjs"
published: "2025-12-31"
updated: "2026-03-28"
categories:
  - "Next.js"
tags:
  - "n8n chat widget"
  - "Next.js chat integration"
  - "createChat"
  - "React Strict Mode"
  - "hydration warnings"
  - "webhook URL"
  - "CSS variables logo"
  - "@n8n/chat package"
  - "client component Next.js"
  - "secure env vars"
  - "TypeScript chat component"
llm-intent: "how-to"
audience-level: "intermediate"
llm-purpose: "n8n chat widget in Next.js: follow this production-ready setup to avoid hydration issues, secure webhook env vars, and display your branded logo—try it…"
llm-prereqs:
  - "@n8n/chat"
  - "Next.js"
  - "React"
  - "TypeScript"
  - "pnpm"
  - "npm"
  - "CSS variables"
---

**Summary Triples**
- (Integrate n8n Chat Widget into Next.js (Production-Ready), expresses-intent, how-to)
- (Integrate n8n Chat Widget into Next.js (Production-Ready), covers-topic, n8n chat widget)
- (Integrate n8n Chat Widget into Next.js (Production-Ready), provides-guidance-for, n8n chat widget in Next.js: follow this production-ready setup to avoid hydration issues, secure webhook env vars, and display your branded logo—try it…)

### {GOAL}
n8n chat widget in Next.js: follow this production-ready setup to avoid hydration issues, secure webhook env vars, and display your branded logo—try it…

### {PREREQS}
- @n8n/chat
- Next.js
- React
- TypeScript
- pnpm
- npm
- CSS variables

### {STEPS}
1. Install the @n8n/chat package
2. Create a dedicated client component
3. Inject logo via CSS variables
4. Securely provide the webhook URL
5. Add component to your layout and test

<!-- llm:goal="n8n chat widget in Next.js: follow this production-ready setup to avoid hydration issues, secure webhook env vars, and display your branded logo—try it…" -->
<!-- llm:prereq="@n8n/chat" -->
<!-- llm:prereq="Next.js" -->
<!-- llm:prereq="React" -->
<!-- llm:prereq="TypeScript" -->
<!-- llm:prereq="pnpm" -->
<!-- llm:prereq="npm" -->
<!-- llm:prereq="CSS variables" -->

# Integrate n8n Chat Widget into Next.js (Production-Ready)
> n8n chat widget in Next.js: follow this production-ready setup to avoid hydration issues, secure webhook env vars, and display your branded logo—try it…
Matija Žiberna · 2025-12-31

Building a custom chatbot UI from scratch is fun, but sometimes you just need something that works immediately and connects seamlessly to your automation workflows. I recently needed to replace a legacy chatbot in a Next.js application with the native [n8n chat widget](https://www.npmjs.com/package/@n8n/chat), but I ran into a few specific challenges: proper lifecycle management in React, handling environment variables securely, and—most annoyingly—injecting a custom brand logo into a header that doesn't natively support it.

Here is the robust, production-ready setup I developed to solve these issues.

## 1. Installation

First, pull in the official package. We're using the specific `@n8n/chat` package which provides the lightweight loader.

```bash
pnpm add @n8n/chat
# or
npm install @n8n/chat
```

## 2. The Chat Component

Instead of dropping a `<script>` tag into your `layout.tsx` (which causes hydration warnings) or using a plain `useEffect` that might fire twice in development, we'll build a dedicated client component.

This component handles:
1.  **Initialization**: Loading the widget only once after the component mounts.
2.  **Configuration**: Passing the webhook URL securely.
3.  **Dynamic Styling**: Using CSS variables to inject a custom logo into the chat header—something the default config object doesn't support.

```typescript
// File: src/components/N8nChat.tsx
'use client';

import { useEffect, useRef } from 'react';
import '@n8n/chat/dist/style.css';
import { createChat } from '@n8n/chat';

// Import your logo asset (Next.js handles the path)
import brandLogo from '@/assets/logo.png';

export const N8nChat = () => {
    // defined to prevent double initialization in Strict Mode
    const initialized = useRef(false);

    useEffect(() => {
        if (initialized.current) return;
        initialized.current = true;

        createChat({
            webhookUrl: process.env.NEXT_PUBLIC_N8N_CHAT_WEBHOOK_URL!,
            mode: 'window',
            target: '#n8n-chat',
            showWelcomeScreen: true,
            initialMessages: [
                'Hi there! 👋',
                'How can I help you today?'
            ],
            i18n: {
                en: {
                    title: 'Support Chat', // We'll hide this with CSS to show the logo
                    subtitle: 'Ask us anything',
                },
            },
        });
    }, []);

    return (
        <>
            {/* Global overrides for the chat widget */}
            <style jsx global>{`
                :root {
                    /* Brand Colors */
                    --chat--color-primary: #003882;
                    --chat--color-secondary: #008ccc;
                    --chat--color-light: #ffffff;
                    --chat--color-dark: #1f2937;
                    
                     /* Component Colors */
                    --chat--header--background: var(--chat--color-primary);
                    --chat--message--user--background: var(--chat--color-primary);
                    --chat--message--bot--background: #f2f4f7;
                }

                /* 
                 * HACK: Inject the logo into the header 
                 * The widget doesn't accept an image for the title, so we:
                 * 1. Target the header title element
                 * 2. Inject a ::before pseudo-element
                 * 3. Use a CSS variable for the image URL
                 */
                .chat-header h1 {
                    display: flex;
                    align-items: center;
                }
                
                .chat-header h1::before {
                    content: '';
                    display: block;
                    width: 120px; /* Adjust based on your logo aspect ratio */
                    height: 50px;
                    min-width: 120px;
                    margin-right: 12px;
                    
                    /* The variable is defined in the inline style below */
                    background-image: var(--chat--header--logo-url);
                    background-size: contain;
                    background-repeat: no-repeat;
                    background-position: left center;
                    flex-shrink: 0;
                }
            `}</style>

            {/* 
              * Container for the chat widget.
              * We define the logo URL here as a CSS variable so it updates 
              * instantly if the React state (logo) changes. 
              */}
            <div 
                id="n8n-chat" 
                style={{ 
                    '--chat--header--logo-url': `url('${brandLogo.src}')` 
                } as React.CSSProperties} 
            />
        </>
    );
};
```

### Key Concepts
- **`useRef(false)`**: In React Strict Mode (development), effects run twice. This ref ensures we only call `createChat` once.
- **CSS Variable Injection**: We pass the imported image URL (`brandLogo.src`) into the DOM via the `--chat--header--logo-url` Custom Property. This allows CSS to access the dynamic Javascript asset path.
- **Deep Selector**: The `.chat-header h1` selector targets the internal structure of the rendered widget. This is brittle if the library changes significantly, but stable for the current version.

## 3. Integration into Layout

Now, simply drop this component into your root layout. Since we marked it `'use client'`, it will work perfectly inside your Server Component layout.

```typescript
// File: src/app/layout.tsx
import { N8nChat } from '@/components/N8nChat';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <N8nChat />
      </body>
    </html>
  );
}
```

## Summary

You now have a chat widget that:
1.  Loads cleanly without hydration errors.
2.  Matches your brand identity using CSS variables.
3.  Displays your actual logo in the header using the CSS injection technique.

This approach gives you the speed of a pre-built widget with the visual polish of a custom component.

Thanks, Matija