> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useinvent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Astro

> Add your Invent assistant to Astro sites

## Overview

Add your Invent AI assistant to any Astro site. Perfect for content-focused websites with islands architecture and multi-framework support.

## Installation

<Steps>
  <Step title="Create Assistant Component">
    Astro supports multiple frameworks. Choose your preferred approach:

    #### Pure Astro Component

    ```astro theme={"system"}
    ---
    // 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/embed.js" async defer></script>
    ```

    #### React Component (with React integration)

    ```tsx theme={"system"}
    // 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/embed.js"
            async
            defer
          />
        </>
      );
    }
    ```
  </Step>

  <Step title="Add to Layout">
    ```astro theme={"system"}
    ---
    // 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>
    ```
  </Step>

  <Step title="Configure Environment">
    Add to `.env`:

    ```bash theme={"system"}
    PUBLIC_INVENT_ASSISTANT_ID=ast_YOUR_ASSISTANT_ID
    INVENT_SECRET_KEY=your_secret_key_here
    ```
  </Step>
</Steps>

## User Authentication

<Warning>
  **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.
</Warning>

### Server-Side Hash Generation

Use Astro API endpoints:

```typescript theme={"system"}
// 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

```astro theme={"system"}
---
// 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:

```astro theme={"system"}
---
// 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:

```typescript theme={"system"}
// 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:

```astro theme={"system"}
---
// 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

<CardGroup cols={2}>
  <Card title="Islands Architecture" icon="island-tropical">
    Perfect for partial hydration
  </Card>

  <Card title="Multi-Framework" icon="layer-group">
    Use React, Vue, Svelte, or pure Astro
  </Card>

  <Card title="Content Collections" icon="book-open">
    Integrate with content data
  </Card>

  <Card title="SSG & SSR" icon="bolt">
    Works with static and server modes
  </Card>
</CardGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Script not loading">
    **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
  </Accordion>

  <Accordion title="Hydration issues">
    **Solutions:**

    * Use appropriate client directive (client:load, client:visible)
    * Ensure script loads after component hydrates
    * Check for SSR/client mismatches
  </Accordion>

  <Accordion title="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`
  </Accordion>
</AccordionGroup>

## Best Practices

<Note>
  * 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
</Note>
