# Frameworks

> Where the two lines go in Next.js, Nuxt, React, Vue, Angular, WordPress, Tilda and Bitrix.

Every stack uses the same two lines ([Install on a site](https://tolkly.io/docs/en/install.md)). Add the script once and
the element once, in the layout all pages share. Replace `your-project` with the project's id.

## Plain HTML

Add the lines before `</body>` on every page, or in the template that builds them:

```html
  <script type="module" src="https://api.tolkly.io/widget.js"></script>
  <tolkly-chat endpoint="https://api.tolkly.io" project="your-project"></tolkly-chat>
</body>
```

Other server templates work the same way: Laravel's Blade layout, Django's `base.html`, Rails'
`application.html.erb`. Put the lines before `</body>` of the base layout.

## Next.js (App Router)
The root layout stays mounted when the visitor navigates. Put the chat there:

```tsx
// app/layout.tsx
import Script from 'next/script'
import type { ReactNode } from 'react'

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <tolkly-chat endpoint="https://api.tolkly.io" project="your-project"></tolkly-chat>
        <Script type="module" src="https://api.tolkly.io/widget.js" strategy="afterInteractive" />
      </body>
    </html>
  )
}
```

- `next/script` passes `type="module"` on to the script it adds.
- With `afterInteractive`, the widget runs after React hydrates the page.
- React 19 renders the custom element with its attributes as written.
- Add the [type declaration](#typescript-and-jsx) below, or TypeScript rejects `<tolkly-chat>`.

## Next.js (Pages Router)
`_app` wraps every page and stays mounted on navigation:

```tsx
// pages/_app.tsx
import type { AppProps } from 'next/app'
import Script from 'next/script'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Component {...pageProps} />
      <tolkly-chat endpoint="https://api.tolkly.io" project="your-project"></tolkly-chat>
      <Script type="module" src="https://api.tolkly.io/widget.js" strategy="afterInteractive" />
    </>
  )
}
```

## TypeScript and JSX
React 19 types read intrinsic elements from `React.JSX`. Declare the tag once in any `.d.ts` file
that your `tsconfig.json` includes, for example `tolkly.d.ts`:

```ts
import type { DetailedHTMLProps, HTMLAttributes } from 'react'

type TolklyChatProps = DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> & {
  endpoint?: string
  project?: string
  name?: string
  greeting?: string
  handoff?: string
  teaser?: string
  position?: 'left' | 'right'
  theme?: 'site' | 'system' | 'light' | 'dark'
  'send-icon'?: 'arrow' | 'plane' | 'forward'
  collapse?: string
  'data-identity'?: string
}

declare module 'react' {
  namespace JSX {
    interface IntrinsicElements {
      'tolkly-chat': TolklyChatProps
    }
  }
}
```

The `import` makes the file a module. Thanks to it, `declare module 'react'` adds to React's
types instead of replacing them. `HTMLAttributes` already covers `lang`, `nonce`, `style` and
`className`.

Do not pass `open` and `inline` in JSX. If the element has a DOM property with the same name,
React 19 sets the prop as that property. `<tolkly-chat>` has an `open()` method and a read-only
`inline` property. Write these attributes in `index.html` instead, or call `open({ focus: false })`
on the element from an effect.

## Nuxt 3
Load the script from `nuxt.config.ts` and tell Vue the tag is a custom element:

```ts
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [{ src: 'https://api.tolkly.io/widget.js', type: 'module' }],
    },
  },
  vue: {
    compilerOptions: {
      isCustomElement: (tag) => tag.startsWith('tolkly-'),
    },
  },
})
```

Then put the element in `app.vue`, outside `<NuxtPage>`:

```vue
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
  <tolkly-chat endpoint="https://api.tolkly.io" project="your-project"></tolkly-chat>
</template>
```

## React with Vite
Put both lines in `index.html`, before `</body>`, outside the `#root` element that React renders
into. You do not need to change the React code. If you prefer to render `<tolkly-chat>` from JSX,
render it once in the root component and add the [type declaration](#typescript-and-jsx).

## Vue 3 with Vite
Put both lines in `index.html`, before `</body>`, outside the `#app` element. If you prefer to use
the tag in a Vue template, tell the compiler it is a custom element:

```ts
// vite.config.ts
import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: { isCustomElement: (tag) => tag.startsWith('tolkly-') },
      },
    }),
  ],
})
```

## Angular

Put both lines in `src/index.html`, before `</body>`, outside the app's root element. Do not add
the script to `scripts` in `angular.json`. That bundles it as a classic script, and it will not
run.

If you prefer to use the tag in a component template, allow custom elements there:

```ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppComponent {}
```

## WordPress

Add the lines just before `</body>` in your theme's `footer.php`. Use a child theme, so a theme
update does not remove them. Or add them from the theme's `functions.php`:

```php
add_action('wp_footer', function () {
    echo '<script type="module" src="https://api.tolkly.io/widget.js"></script>';
    echo '<tolkly-chat endpoint="https://api.tolkly.io" project="your-project"></tolkly-chat>';
});
```

No access to theme files? Any plugin that adds custom code before `</body>` on every page will
do. Then clear the page cache, if the site has one.

## Tilda

Add both lines to an HTML code block (T123). Put the block in a footer that every page uses, or on
each page where the chat should show. Then publish the pages again.

## 1C-Bitrix
Add the lines just before `</body>` in the site template's `footer.php`. It is at
`/local/templates/<template>/footer.php` or `/bitrix/templates/<template>/footer.php`. If the
template is cached, clear the site cache afterwards.
