# Add the widget to a React or Next.js app

> Mount the widget with one component, drive it with a hook, and get typed attributes in Vue and Svelte.

The `@helpai/elements` package comes with a React kit: a component that adds the widget and a hook that drives it from any component. It works with server rendering, includes its TypeScript types, and needs React 18 or later.

## 1. Install the package

```bash
npm install @helpai/elements
```

## 2. Add the widget once, in your root layout

```tsx
// app/layout.tsx (Next.js App Router)
import { HelpAI } from '@helpai/elements/react';

type Props = { children: React.ReactNode };

export default function RootLayout({ children }: Props) {
  return (
    <html lang='en'>
      <body>
        {children}
        <HelpAI
          publicKey='agent_pk_...'
          aiAgentDeploymentId='...'
          theme='host'
        />
      </body>
    </html>
  );
}
```

Copy the public key and deployment ID from the deployment's **Install** tab. `<HelpAI />` takes every widget option as a prop, the same as `init()`: `theme`, `launcher`, `userToken`, `widgetId` and more. It draws nothing itself: the widget appears once the page is running in the browser, a changed prop updates it in place, and removing the component removes the widget.

In the App Router, a server layout can only pass plain values. To use callback props such as `onOpen` or `onMessage`, render `<HelpAI />` from a `'use client'` component of your own.

## 3. Drive it from any component

```tsx
'use client';

import { useHelpAI } from '@helpai/elements/react';

export function PricingHelp() {
  const { ask, open, state } = useHelpAI();
  return (
    <>
      <button onClick={() => ask('Which plan fits us?')}>
        Ask about plans
      </button>
      <button onClick={() => open({ to: 'help' })}>
        Help ({state.unread} unread)
      </button>
    </>
  );
}
```

`useHelpAI()` returns `state`, `ask`, `open`, `close`, `reset` and `track`, and re-renders your component when the state changes. The calls work the same as in [Control the widget with JavaScript](https://docs.help.ai/customize-and-extend-the-widget/control-the-widget-with-javascript). Before `<HelpAI />` has mounted they do nothing; `state.ready` turns true once they act.

<Tip>
Import everything from `@helpai/elements/react`, types included, so every component talks to the same widget.
</Tip>

## Signed-in users and a second widget

- **Signed-in users:** pass a function that fetches your token as `userToken`. Keep it the same function for the same user; removing the prop signs them out. See [Verify signed-in users](https://docs.help.ai/customize-and-extend-the-widget/verify-signed-in-users).
- **Two widgets:** give each `<HelpAI widgetId='support' … />` its own id, and read it with `useHelpAI('support')`. See [Run two widgets on one page](https://docs.help.ai/customize-and-extend-the-widget/run-two-widgets-on-one-page).

## Vue, Svelte and other frameworks

Use the `<web-ai-chat>` element. Register it once:

```ts
import '@helpai/elements/web-component';
```

With server rendering, such as Nuxt or SvelteKit, import it only in browser code: a Nuxt plugin whose file name ends in `.client.ts`, or `onMount` in SvelteKit, with `await import('@helpai/elements/web-component')`. The element can't be defined on the server.

Then add its types once, so your editor checks every attribute:

```ts
// Vue 3. For Svelte 5: /jsx/svelte. For React 19: /jsx/react
import type {} from '@helpai/elements/jsx/vue';
```

Vue also needs `web-ai-chat` marked as a custom element. With Vite:

```js
// vite.config.js
import vue from '@vitejs/plugin-vue';

export default {
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag === 'web-ai-chat',
        },
      },
    }),
  ],
};
```

On the element, wait with `await element.ready` and listen with `element.on(…)`. Listeners end when the element leaves the page.
