Overview
Add your Invent AI assistant to any Astro site. Perfect for content-focused websites with islands architecture and multi-framework support.
Installation
Create Assistant Component
Astro supports multiple frameworks. Choose your preferred approach: Pure Astro Component ---
// src/components/InventAssistant.astro
export interface Props {
assistantId : string ;
themeAppearance ?: 'auto' | 'light' | 'dark' ;
themeButtonBackgroundColor ?: string ;
themeButtonColor ?: string ;
userId ?: string ;
userName ?: string ;
userHash ?: string ;
userAvatar ?: string ;
}
const {
assistantId ,
themeAppearance = 'auto' ,
themeButtonBackgroundColor ,
themeButtonColor ,
userId ,
userName ,
userHash ,
userAvatar ,
} = Astro . props ;
---
< 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 ></ script >
React Component (with React integration) // src/components/InventAssistant.tsx
interface Props {
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 ,
} : Props ) {
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 Layout
---
// src/layouts/Layout.astro
import InventAssistant from '../components/InventAssistant.astro' ;
// Or if using React:
// import InventAssistant from '../components/InventAssistant.tsx';
const assistantId = import . meta . env . PUBLIC_INVENT_ASSISTANT_ID ;
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width" />
< title > Your Site </ title >
</ head >
< body >
< slot />
< InventAssistant assistantId = { assistantId } />
<!-- If using React component, add client directive -->
<!-- <InventAssistant client:load assistantId={assistantId} /> -->
</ body >
</ html >
Configure Environment
Add to .env
: PUBLIC_INVENT_ASSISTANT_ID = ast_YOUR_ASSISTANT_ID
INVENT_SECRET_KEY = your_secret_key_here
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
Use Astro API endpoints:
// src/pages/api/user-hash.ts
import type { APIRoute } from 'astro' ;
import crypto from 'crypto' ;
export const GET : APIRoute = async ({ locals }) => {
// Get user from your auth system
const user = locals . user ;
if ( ! user ) {
return new Response (
JSON . stringify ({ error: 'Unauthorized' }),
{ status: 401 , headers: { 'Content-Type' : 'application/json' } }
);
}
const secretKey = import . meta . env . INVENT_SECRET_KEY ;
const userId = user . id ;
const userHash = crypto
. createHmac ( 'sha256' , secretKey )
. update ( userId )
. digest ( 'hex' );
return new Response (
JSON . stringify ({
userId ,
userName: user . name ,
userAvatar: user . avatar ,
userHash ,
}),
{ status: 200 , headers: { 'Content-Type' : 'application/json' } }
);
};
Using in Layout with Server-Side Data
---
// src/layouts/Layout.astro
import InventAssistant from '../components/InventAssistant.astro' ;
import crypto from 'crypto' ;
const assistantId = import . meta . env . PUBLIC_INVENT_ASSISTANT_ID ;
// Get user from locals (set by middleware)
const user = Astro . locals . user ;
let userData = null ;
if ( user ) {
const secretKey = import . meta . env . INVENT_SECRET_KEY ;
const userId = user . id ;
const userHash = crypto
. createHmac ( 'sha256' , secretKey )
. update ( userId )
. digest ( 'hex' );
userData = {
userId ,
userName: user . name ,
userAvatar: user . avatar ,
userHash ,
};
}
---
< ! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width" />
< title > Your Site </ title >
</ head >
< body >
< slot />
< InventAssistant
assistantId = { assistantId }
{ ... userData }
/>
</ body >
</ html >
Content Collections Integration
Pass content data for better context:
---
// src/pages/blog/[slug].astro
import { getCollection , getEntry } from 'astro:content' ;
import Layout from '../../layouts/Layout.astro' ;
export async function getStaticPaths () {
const posts = await getCollection ( 'blog' );
return posts . map (( post ) => ({
params: { slug: post . slug },
props: { post },
}));
}
const { post } = Astro . props ;
const { Content } = await post . render ();
---
< Layout title = { post . data . title } >
< article >
< h1 > { post . data . title } </ h1 >
< Content />
</ article >
< script define:vars = { { post: post . data } } >
window . inventContext = {
pageType: 'blog-post' ,
title: post . title ,
author: post . author ,
category: post . category ,
};
</ script >
</ Layout >
TypeScript Support
Add type declarations:
// src/env.d.ts
/// < reference types = "astro/client" />
declare namespace App {
interface Locals {
user ?: {
id : string ;
name : string ;
avatar ?: string ;
};
}
}
interface ImportMetaEnv {
readonly PUBLIC_INVENT_ASSISTANT_ID : string ;
readonly INVENT_SECRET_KEY : string ;
}
interface ImportMeta {
readonly env : ImportMetaEnv ;
}
declare global {
interface Window {
inventContext ?: Record < string , any >;
}
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 ;
};
}
}
}
Using with React/Vue/Svelte Islands
Astro’s island architecture allows mixing frameworks:
---
// src/pages/index.astro
import Layout from '../layouts/Layout.astro' ;
import ReactComponent from '../components/ReactComponent.tsx' ;
import VueComponent from '../components/VueComponent.vue' ;
---
< Layout >
< h1 > Welcome </ h1 >
<!-- React island with client:load directive -->
< ReactComponent client:load />
<!-- Vue island with client:visible directive -->
< VueComponent client:visible />
</ Layout >
Tips for Astro
Islands Architecture Perfect for partial hydration
Multi-Framework Use React, Vue, Svelte, or pure Astro
Content Collections Integrate with content data
SSG & SSR Works with static and server modes
Troubleshooting
Solutions:
Ensure script tag is in component, not layout head
Check client directives if using framework components
Verify URL is correct
Check browser console for errors
Solutions:
Use appropriate client directive (client:load, client:visible)
Ensure script loads after component hydrates
Check for SSR/client mismatches
Environment variables not found
Solutions:
Restart dev server after adding env variables
Use PUBLIC_
prefix for client-accessible variables
Keep secret key without prefix (server-only)
Use import.meta.env
not process.env
Best Practices
Use API routes or server-side code for hash generation
Keep secret key in .env
(never commit)
Use PUBLIC_
prefix only for non-sensitive variables
Choose appropriate client directives for islands
Leverage Astro’s content collections for context