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

# Install Orwel in Next.js, SvelteKit, Astro, Nuxt, Vite and more

> Where orwel.init() goes in each framework, which environment variable name it reads, how to keep it from running twice, and how to check the install in under a minute.

Whatever the framework, an Orwel install is three decisions:

1. **Where `orwel.init()` runs:** once, in the browser, as early as possible (a root layout, a client entry or a plugin).
2. **Where the key lives:** an environment variable the browser can read, which means a framework specific public prefix.
3. **Which key:** the development key (`orwel_dev_...`) on your machine and in previews, the production API key (`orwel_...`) in production. See [Dev mode](https://www.orwel.io/docs/guides/dev-mode.md).

Both keys are public by design: they can send data but never read it, so they are safe in browser code. Secret keys (`orwel_sk_...`) must never be used here.

> [!TIP]
>
> The pages at [orwel.io/install](https://orwel.io/install) fill in your own key and walk through each platform step by step. Or let a coding agent do it with the [AI skill](https://www.orwel.io/docs/sdk/ai-skill.md).

## Public variable names

The variable must carry the prefix your framework exposes to the browser. A missing prefix is the most common reason an install sends nothing: the value is `undefined` in the browser and `init` never runs.

| Framework | Variable | Read it with |
| - | - | - |
| Next.js | `NEXT_PUBLIC_ORWEL_KEY` | `process.env.NEXT_PUBLIC_ORWEL_KEY` |
| SvelteKit | `PUBLIC_ORWEL_KEY` | `import { PUBLIC_ORWEL_KEY } from '$env/static/public'` |
| Astro | `PUBLIC_ORWEL_KEY` | `import.meta.env.PUBLIC_ORWEL_KEY` |
| Vite (React, Vue, Svelte, Solid) | `VITE_ORWEL_KEY` | `import.meta.env.VITE_ORWEL_KEY` |
| React Router 7 / Remix on Vite | `VITE_ORWEL_KEY` | `import.meta.env.VITE_ORWEL_KEY` |
| Nuxt | `NUXT_PUBLIC_ORWEL_KEY` | `useRuntimeConfig().public.orwelKey` |

Public variables are inlined at build time in most frameworks, so set them in your host **before** the build, and redeploy after changing them.

## Initialize once

`orwel.init()` sets up the session, the queue and the page listeners. Call it exactly once per page load. React Strict Mode runs effects twice in development, and some layouts mount twice during hydration, so guard the call:

```ts lib/orwel.ts theme={null}
import orwel from 'orwel'

let started = false

export function startOrwel(apiKey: string | undefined) {
  if (started || !apiKey) return
  started = true
  orwel.init({ apiKey })
}
```

Importing `orwel` on the server is safe: outside the browser every method is a no-op. Only `init` in the browser starts tracking.

## Frameworks

> [tab] Next.js (App Router)
>
> ```bash theme={null}
> npm i orwel@latest
> ```
>
> ```bash .env.local theme={null}
> NEXT_PUBLIC_ORWEL_KEY=orwel_dev_...
> ```
>
> ```tsx app/orwel.tsx theme={null}
> 'use client'
> import { useEffect } from 'react'
> import { startOrwel } from '@/lib/orwel'
>
> export function Orwel() {
>   useEffect(() => {
>     startOrwel(process.env.NEXT_PUBLIC_ORWEL_KEY)
>   }, [])
>   return null
> }
> ```
>
> Render `<Orwel />` once inside `<body>` in `app/layout.tsx`. Conversions go in client components (`'use client'`), never in server actions. [Full guide](https://orwel.io/install/nextjs)

> [tab] Next.js (Pages Router)
>
> ```tsx pages/_app.tsx theme={null}
> import type { AppProps } from 'next/app'
> import { useEffect } from 'react'
> import { startOrwel } from '@/lib/orwel'
>
> export default function App({ Component, pageProps }: AppProps) {
>   useEffect(() => {
>     startOrwel(process.env.NEXT_PUBLIC_ORWEL_KEY)
>   }, [])
>   return <Component {...pageProps} />
> }
> ```
>
> Same variable as the App Router: `NEXT_PUBLIC_ORWEL_KEY`.

> [tab] SvelteKit
>
> ```bash theme={null}
> npm i orwel@latest
> ```
>
> ```bash .env theme={null}
> PUBLIC_ORWEL_KEY=orwel_dev_...
> ```
>
> ```svelte src/routes/+layout.svelte theme={null}
> <script>
>   import { onMount } from 'svelte'
>   import { PUBLIC_ORWEL_KEY } from '$env/static/public'
>   import { startOrwel } from '$lib/orwel'
>
>   let { children } = $props()
>
>   onMount(() => startOrwel(PUBLIC_ORWEL_KEY))
> </script>
>
> {@render children()}
> ```
>
> `onMount` only runs in the browser, so server rendering is unaffected. On Svelte 4 keep `<slot />` instead of `{@render children()}`. [Full guide](https://orwel.io/install/sveltekit)

> [tab] Astro
>
> ```bash theme={null}
> npm i orwel@latest
> ```
>
> ```bash .env theme={null}
> PUBLIC_ORWEL_KEY=orwel_dev_...
> ```
>
> ```astro src/layouts/Base.astro theme={null}
> <script>
>   import orwel from 'orwel'
>   const apiKey = import.meta.env.PUBLIC_ORWEL_KEY
>   if (apiKey) orwel.init({ apiKey })
> </script>
> ```
>
> Astro bundles and runs this script once per page load. With View Transitions the page is not reloaded on navigation, which is what you want: the session continues. [Full guide](https://orwel.io/install/astro)

> [tab] Vite
>
> ```bash theme={null}
> npm i orwel@latest
> ```
>
> ```bash .env theme={null}
> VITE_ORWEL_KEY=orwel_dev_...
> ```
>
> ```ts src/main.ts theme={null}
> import { startOrwel } from './lib/orwel'
>
> startOrwel(import.meta.env.VITE_ORWEL_KEY)
> // then mount your React, Vue, Svelte or Solid app
> ```
>
> Call it before mounting the app. [Full guide](https://orwel.io/install/vite)

> [tab] React Router 7 and Remix
>
> ```tsx app/root.tsx theme={null}
> import { useEffect } from 'react'
> import { Outlet } from 'react-router'
> import { startOrwel } from './lib/orwel'
>
> export default function App() {
>   useEffect(() => {
>     startOrwel(import.meta.env.VITE_ORWEL_KEY)
>   }, [])
>   return <Outlet />
> }
> ```
>
> With the Vite plugin, `VITE_` variables are available in client code. Keep `<Outlet />` (and the rest of your layout) as it was; only the effect is new.

> [tab] Nuxt
>
> ```bash .env theme={null}
> NUXT_PUBLIC_ORWEL_KEY=orwel_dev_...
> ```
>
> ```ts nuxt.config.ts theme={null}
> export default defineNuxtConfig({
>   runtimeConfig: { public: { orwelKey: '' } }
> })
> ```
>
> ```ts plugins/orwel.client.ts theme={null}
> import orwel from 'orwel'
>
> export default defineNuxtPlugin(() => {
>   const apiKey = useRuntimeConfig().public.orwelKey
>   if (apiKey) orwel.init({ apiKey })
> })
> ```
>
> The `.client` suffix makes Nuxt run the plugin only in the browser, once per page load. `NUXT_PUBLIC_ORWEL_KEY` overrides `orwelKey` at runtime.

## Sites without a build step

For plain HTML and website builders, add the CDN script to the `<head>` of every page. The production API key goes in the snippet itself; it can only send data.

```html theme={null}
<script src="https://cdn.jsdelivr.net/npm/orwel@1/dist/orwel.umd.js"></script>
<script>
  orwel.init({ apiKey: 'orwel_...' })
</script>
```

`orwel@1` always loads the latest 1.x release, so these sites get SDK fixes without a change.

| Platform | Where to paste it | Guide |
| - | - | - |
| HTML | Before `</head>` in every page | [orwel.io/install/html](https://orwel.io/install/html) |
| Shopify | Online Store, Themes, Edit code, `theme.liquid` | [orwel.io/install/shopify](https://orwel.io/install/shopify) |
| WordPress | A header snippet with the WPCode plugin | [orwel.io/install/wordpress](https://orwel.io/install/wordpress) |
| Webflow | Site settings, Custom code, Head code | [orwel.io/install/webflow](https://orwel.io/install/webflow) |
| Lovable | Ask the Lovable chat, with the key in Secrets as `VITE_ORWEL_KEY` | [orwel.io/install/lovable](https://orwel.io/install/lovable) |

## Check the install

**1. Look for the global**

Open your site, then the browser console, and type `window.orwel`. An object means the SDK loaded. `undefined` means the script or import never ran on this page.

**2. Look at the network**

In the Network tab filter by `api.orwel.io`. Within a few seconds of a page load you should see a request with status `200`. Nothing there usually means the key variable is `undefined`; `401` means the key is wrong.

**3. Watch it arrive**

With the development key, open [Dev mode](https://www.orwel.io/docs/guides/dev-mode.md) in the dashboard: the session appears within seconds, with any problem explained.

## Then capture conversions

```ts theme={null}
const { success } = await orwel.conversion('contact_form', { email, name })
```

See [Conversions](https://www.orwel.io/docs/sdk/conversions.md) for examples per form and the mistakes that lose leads.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.