Overview
Add your Invent AI assistant to any Next.js application (App Router or Pages Router). Perfect for React-based applications with server-side rendering and static generation.
Installation
App Router (Next.js 13+)
Create Assistant Component
Create a client component for the assistant: // components/InventAssistant.tsx
'use client' ;
interface InventAssistantProps {
assistantId : string ;
themeAppearance ?: 'auto' | 'light' | 'dark' ;
themeButtonBackgroundColor ?: string ;
themeButtonColor ?: string ;
userId ?: string ;
userName ?: string ;
userHash ?: string ;
userAvatar ?: string ;
}
export default function InventAssistant ({
assistantId ,
themeAppearance = 'auto' ,
themeButtonBackgroundColor ,
themeButtonColor ,
userId ,
userName ,
userHash ,
userAvatar ,
} : InventAssistantProps ) {
return (
<>
< invent-assistant
assistant-id = { assistantId }
theme-appearance = { themeAppearance }
{ ... ( themeButtonBackgroundColor && {
'theme-button-background-color' : themeButtonBackgroundColor ,
}) }
{ ... ( themeButtonColor && {
'theme-button-color' : themeButtonColor ,
}) }
{ ... ( userId && { 'user-id' : userId }) }
{ ... ( userName && { 'user-name' : userName }) }
{ ... ( userHash && { 'user-hash' : userHash }) }
{ ... ( userAvatar && { 'user-avatar' : userAvatar }) }
/>
< script
type = "text/javascript"
src = "https://www.useinvent.com/button.js"
async
defer
/>
</>
);
}
Add to Root Layout
Import and use in your root layout: // app/layout.tsx
import InventAssistant from '@/components/InventAssistant' ;
export default function RootLayout ({
children ,
} : {
children : React . ReactNode ;
}) {
return (
< html lang = "en" >
< body >
{ children }
< InventAssistant assistantId = { process . env . NEXT_PUBLIC_INVENT_ASSISTANT_ID ! } />
</ body >
</ html >
);
}
Add Environment Variable
Add to your .env.local
: NEXT_PUBLIC_INVENT_ASSISTANT_ID = ast_YOUR_ASSISTANT_ID
Pages Router (Next.js 12 and below)
Create Assistant Component
// components/InventAssistant.tsx
export default function InventAssistant ({ assistantId } : { assistantId : string }) {
return (
<>
< invent-assistant assistant-id = { assistantId } />
< script
type = "text/javascript"
src = "https://www.useinvent.com/button.js"
async
defer
/>
</>
);
}
Add to _app.tsx
// pages/_app.tsx
import type { AppProps } from 'next/app' ;
import InventAssistant from '@/components/InventAssistant' ;
export default function App ({ Component , pageProps } : AppProps ) {
return (
<>
< Component { ... pageProps } />
< InventAssistant assistantId = { process . env . NEXT_PUBLIC_INVENT_ASSISTANT_ID ! } />
</>
);
}
User Authentication
Security Requirement: When using any user-*
attributes (user-id
, user-name
, user-avatar
), you must also provide user-hash
. Both user-id
and user-hash
must be provided together, or neither should be provided. The user-hash
must be generated on your backend using HMAC-SHA256 with your assistant’s secret key. Never expose the secret key to the client.
Server-Side Hash Generation
Next.js is perfect for secure hash generation with Server Components or API Routes.
Using Server Components (App Router)
// app/layout.tsx
import { cookies } from 'next/headers' ;
import { generateUserHash } from '@/lib/auth' ;
import InventAssistant from '@/components/InventAssistant' ;
async function getUserData () {
// Get user from your auth system (NextAuth, Clerk, etc.)
const session = await getSession ();
if ( ! session ?. user ) {
return null ;
}
const userId = session . user . id ;
const userHash = await generateUserHash ( userId );
return {
userId ,
userName: session . user . name ,
userAvatar: session . user . image ,
userHash ,
};
}
export default async function RootLayout ({
children ,
} : {
children : React . ReactNode ;
}) {
const userData = await getUserData ();
return (
< html lang = "en" >
< body >
{ children }
< InventAssistant
assistantId = { process . env . NEXT_PUBLIC_INVENT_ASSISTANT_ID ! }
{ ... userData }
/>
</ body >
</ html >
);
}
Hash Generation Helper
// lib/auth.ts
import crypto from 'crypto' ;
export async function generateUserHash ( userId : string ) : Promise < string > {
const secretKey = process . env . INVENT_SECRET_KEY ;
if ( ! secretKey ) {
throw new Error ( 'INVENT_SECRET_KEY is not set' );
}
return crypto
. createHmac ( 'sha256' , secretKey )
. update ( userId )
. digest ( 'hex' );
}
Using API Routes (Pages Router)
// pages/api/user-hash.ts
import type { NextApiRequest , NextApiResponse } from 'next' ;
import crypto from 'crypto' ;
import { getSession } from 'next-auth/react' ;
export default async function handler (
req : NextApiRequest ,
res : NextApiResponse
) {
const session = await getSession ({ req });
if ( ! session ?. user ) {
return res . status ( 401 ). json ({ error: 'Unauthorized' });
}
const userId = session . user . id ;
const secretKey = process . env . INVENT_SECRET_KEY ! ;
const userHash = crypto
. createHmac ( 'sha256' , secretKey )
. update ( userId )
. digest ( 'hex' );
res . status ( 200 ). json ({
userId ,
userName: session . user . name ,
userAvatar: session . user . image ,
userHash ,
});
}
Then fetch this data in your component:
// components/InventAssistant.tsx
'use client' ;
import { useEffect , useState } from 'react' ;
export default function InventAssistant ({ assistantId } : { assistantId : string }) {
const [ userData , setUserData ] = useState ( null );
useEffect (() => {
fetch ( '/api/user-hash' )
. then (( res ) => res . json ())
. then ( setUserData )
. catch ( console . error );
}, []);
// ... rest of component
}
Integration with Auth Libraries
NextAuth.js
// app/layout.tsx
import { getServerSession } from 'next-auth' ;
import { authOptions } from '@/lib/auth' ;
import { generateUserHash } from '@/lib/auth' ;
export default async function RootLayout ({ children }) {
const session = await getServerSession ( authOptions );
let userData = null ;
if ( session ?. user ) {
userData = {
userId: session . user . id ,
userName: session . user . name ,
userAvatar: session . user . image ,
userHash: await generateUserHash ( session . user . id ),
};
}
return (
< html >
< body >
{ children }
< InventAssistant
assistantId = { process . env . NEXT_PUBLIC_INVENT_ASSISTANT_ID ! }
{ ... userData }
/>
</ body >
</ html >
);
}
Clerk
// app/layout.tsx
import { currentUser } from '@clerk/nextjs' ;
import { generateUserHash } from '@/lib/auth' ;
export default async function RootLayout ({ children }) {
const user = await currentUser ();
let userData = null ;
if ( user ) {
userData = {
userId: user . id ,
userName: user . fullName ,
userAvatar: user . imageUrl ,
userHash: await generateUserHash ( user . id ),
};
}
return (
< html >
< body >
{ children }
< InventAssistant
assistantId = { process . env . NEXT_PUBLIC_INVENT_ASSISTANT_ID ! }
{ ... userData }
/>
</ body >
</ html >
);
}
TypeScript Support
Add type declarations for the custom element:
// types/invent-assistant.d.ts
declare namespace JSX {
interface IntrinsicElements {
'invent-assistant' : {
'assistant-id' : string ;
'theme-appearance' ?: 'auto' | 'light' | 'dark' ;
'theme-button-background-color' ?: string ;
'theme-button-color' ?: string ;
'user-id' ?: string ;
'user-name' ?: string ;
'user-hash' ?: string ;
'user-avatar' ?: string ;
};
}
}
Tips for Next.js
Server Components Use Server Components for secure hash generation
Environment Variables Keep INVENT_SECRET_KEY server-side only
TypeScript Add type definitions for better DX
SEO Friendly No impact on SEO or page performance
Troubleshooting
Bubble not appearing in development
Solutions:
Check that the script is loaded after the component mounts
Verify assistant ID is correct
Check browser console for errors
Try clearing Next.js cache: rm -rf .next
TypeScript errors with custom element
Solutions:
Add type declarations file (see TypeScript Support section)
Ensure file is included in tsconfig.json
Restart TypeScript server
Solutions:
Verify secret key is set in environment variables
Ensure hash is generated server-side, not client-side
Check that both user-id and user-hash are provided
Validate HMAC-SHA256 implementation
Best Practices
Always use Server Components or API Routes for hash generation
Store secret key in .env.local
(never commit to git)
Use NEXT_PUBLIC_
prefix only for non-sensitive variables
Test authentication in both development and production
Consider using middleware for auth logic