> ## 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.

# Gatsby

> Add your Invent assistant to Gatsby sites

## Overview

Add your Invent AI assistant to any Gatsby site. Perfect for static websites built with React and GraphQL.

## Installation

<Steps>
  <Step title="Install React Helmet">
    ```bash theme={"system"}
    npm install react-helmet gatsby-plugin-react-helmet
    ```
  </Step>

  <Step title="Configure gatsby-config.js">
    Add the plugin to your `gatsby-config.js`:

    ```javascript theme={"system"}
    module.exports = {
      plugins: [
        'gatsby-plugin-react-helmet',
        // ... other plugins
      ],
    };
    ```
  </Step>

  <Step title="Create Assistant Component">
    ```tsx theme={"system"}
    // src/components/InventAssistant.tsx
    import React from 'react';

    interface InventAssistantProps {
      assistantId: string;
      themeAppearance?: 'auto' | 'light' | 'dark';
      themeButtonBackgroundColor?: string;
      themeButtonColor?: string;
      userId?: string;
      userName?: string;
      userHash?: string;
      userAvatar?: string;
    }

    const InventAssistant: React.FC<InventAssistantProps> = ({
      assistantId,
      themeAppearance = 'auto',
      themeButtonBackgroundColor,
      themeButtonColor,
      userId,
      userName,
      userHash,
      userAvatar,
    }) => {
      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
          />
        </>
      );
    };

    export default InventAssistant;
    ```
  </Step>

  <Step title="Add to Layout">
    ```tsx theme={"system"}
    // src/components/layout.tsx
    import React from 'react';
    import InventAssistant from './InventAssistant';

    const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {
      return (
        <>
          <main>{children}</main>
          <InventAssistant assistantId={process.env.GATSBY_INVENT_ASSISTANT_ID!} />
        </>
      );
    };

    export default Layout;
    ```
  </Step>

  <Step title="Add Environment Variable">
    Create `.env.development` and `.env.production`:

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

### Using Gatsby Functions (Serverless)

Gatsby Functions allow you to generate the hash server-side:

```typescript theme={"system"}
// src/api/user-hash.ts
import type { GatsbyFunctionRequest, GatsbyFunctionResponse } from 'gatsby';
import crypto from 'crypto';

export default function handler(
  req: GatsbyFunctionRequest,
  res: GatsbyFunctionResponse
) {
  // Get user from your auth system
  const userId = req.headers['x-user-id'];

  if (!userId) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  const secretKey = process.env.INVENT_SECRET_KEY!;

  const userHash = crypto
    .createHmac('sha256', secretKey)
    .update(userId as string)
    .digest('hex');

  return res.json({
    userId,
    userHash,
  });
}
```

### Fetching User Data

```tsx theme={"system"}
// src/components/InventAssistantWrapper.tsx
import React, { useEffect, useState } from 'react';
import InventAssistant from './InventAssistant';

const InventAssistantWrapper: React.FC = () => {
  const [userData, setUserData] = useState<{
    userId?: string;
    userName?: string;
    userHash?: string;
    userAvatar?: string;
  } | null>(null);

  useEffect(() => {
    fetch('/api/user-hash')
      .then((res) => res.json())
      .then(setUserData)
      .catch(console.error);
  }, []);

  return (
    <InventAssistant
      assistantId={process.env.GATSBY_INVENT_ASSISTANT_ID!}
      {...userData}
    />
  );
};

export default InventAssistantWrapper;
```

## GraphQL Integration

Pass content data to provide context:

```tsx theme={"system"}
// src/templates/blog-post.tsx
import React, { useEffect } from 'react';
import { graphql } from 'gatsby';
import Layout from '../components/layout';

const BlogPost: React.FC<{ data: any }> = ({ data }) => {
  const post = data.markdownRemark;

  useEffect(() => {
    if (typeof window !== 'undefined') {
      window.inventContext = {
        pageType: 'blog-post',
        title: post.frontmatter.title,
        author: post.frontmatter.author,
        category: post.frontmatter.category,
      };
    }
  }, [post]);

  return (
    <Layout>
      <article>
        <h1>{post.frontmatter.title}</h1>
        <div dangerouslySetInnerHTML={{ __html: post.html }} />
      </article>
    </Layout>
  );
};

export const query = graphql`
  query($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        author
        category
      }
    }
  }
`;

export default BlogPost;
```

## TypeScript Support

Add type declarations:

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

interface Window {
  inventContext?: Record<string, any>;
}
```

## Tips for Gatsby

<CardGroup cols={2}>
  <Card title="Serverless Functions" icon="bolt">
    Use Gatsby Functions for hash generation
  </Card>

  <Card title="GraphQL Context" icon="database">
    Pass content data for better context
  </Card>

  <Card title="Static & Dynamic" icon="rocket">
    Works with SSG and DSG builds
  </Card>

  <Card title="Plugin Ecosystem" icon="puzzle-piece">
    Integrates with Gatsby plugins
  </Card>
</CardGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Script loading multiple times">
    **Solutions:**

    * Check gatsby-browser.js for duplicate script logic
    * Use conditional to prevent duplicate loads
    * Clear .cache and public folders: `gatsby clean`
  </Accordion>

  <Accordion title="Environment variables not working">
    **Solutions:**

    * Prefix public variables with `GATSBY_`
    * Restart development server
    * Check .env files exist
    * Verify gatsby-config.js is reading variables
  </Accordion>

  <Accordion title="Build errors">
    **Solutions:**

    * Ensure typeof window checks for SSR
    * Use dynamic imports if needed
    * Check all dependencies are installed
    * Clear cache: `gatsby clean`
  </Accordion>
</AccordionGroup>

## Best Practices

<Note>
  * Use Gatsby Functions for server-side operations
  * Always check for `window` before accessing it
  * Store secret keys in `.env` files (never commit)
  * Use `GATSBY_` prefix only for public variables
  * Test both development and production builds
</Note>
