This guide shows you how to add Heyo to your Next.js application. Compatible with both App Router (Next.js 13+) and Pages Router.
Install the SDK:
npm install @heyo.so/js
Create a client component components/chat-widget.tsx:
'use client';
import { useEffect } from 'react';
import HEYO from '@heyo.so/js'; // or: import { HEYO } from '@heyo.so/js'
export const ChatWidget = () => {
useEffect(() => {
HEYO.init({ projectId: 'YOUR_PROJECT_ID' });
}, []);
return null;
};
Then add it to your app/layout.tsx:
import { ChatWidget } from '@/components/chat-widget';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<ChatWidget />
</body>
</html>
);
}
Add the Heyo script to app/layout.tsx:
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
<script
src="https://heyo.so/embed/script"
defer
data-project-id="YOUR_PROJECT_ID"
/>
</head>
<body>{children}</body>
</html>
);
}
Install the SDK and create a client component as shown above, then add to pages/_app.tsx:
import { ChatWidget } from '@/components/chat-widget';
export default function App({ Component, pageProps }) {
return (
<>
<Component {...pageProps} />
<ChatWidget />
</>
);
}
Add the script to pages/_document.tsx:
import { Html, Head, Main, NextScript } from 'next/document';
export default function Document() {
return (
<Html lang="en">
<Head>
<script
src="https://heyo.so/embed/script"
defer
data-project-id="YOUR_PROJECT_ID"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
Heyo works seamlessly with TypeScript. Install the SDK for full type safety:
npm install @heyo.so/js
// Both import styles work
import HEYO from '@heyo.so/js';
// or: import { HEYO } from '@heyo.so/js';
HEYO.init({ projectId: 'YOUR_PROJECT_ID' });
Control the widget based on routes or user state:
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
export const ChatWidget = () => {
const pathname = usePathname();
useEffect(() => {
// Hide chat on checkout pages
if (pathname?.includes('/checkout')) {
window.HEYO?.hide();
} else {
window.HEYO?.show();
}
}, [pathname]);
useEffect(() => {
HEYO.init({ projectId: 'YOUR_PROJECT_ID' });
}, []);
return null;
};
Use server components to pass data to the chat widget:
// app/layout.tsx
import { ChatWidget } from '@/components/chat-widget';
import { getUser } from '@/lib/auth';
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const user = await getUser();
return (
<html lang="en">
<body>
{children}
<ChatWidget user={user} />
</body>
</html>
);
}
// components/chat-widget.tsx
'use client';
import { useEffect } from 'react';
import HEYO from '@heyo.so/js'; // or: import { HEYO } from '@heyo.so/js'
export const ChatWidget = ({ user }: { user: any }) => {
useEffect(() => {
HEYO.init({
projectId: 'YOUR_PROJECT_ID',
user: user
? {
name: user.name,
email: user.email,
}
: undefined,
});
}, [user]);
return null;
};
For optimal performance, lazy load the chat widget:
'use client';
import dynamic from 'next/dynamic';
const ChatWidget = dynamic(() => import('@/components/chat-widget'), {
ssr: false,
});
export default ChatWidget;
Heyo is optimized to have minimal impact on your Core Web Vitals:
deferEnsure the script tag is in the <head> of your document, not in a component that might be conditionally rendered.
If using the script tag in a client component, you may see hydration warnings. Use the SDK method instead.
Make sure Heyo is initialized in your root layout, not in individual pages.