[relaybell docs › Install › Vue and Nuxt](https://relaybell.com/docs/install-vue) · [All docs as Markdown](https://relaybell.com/docs.md) · [Index for AI assistants](https://relaybell.com/llms.txt)

# Vue and Nuxt

> Add relaybell to a Vue 3 app (Vite) or a Nuxt 3 site: load the SDK once, serve sw.js from public/, add a subscribe button and link signed-in users.

The browser SDK is a script tag that puts `RelayBell` on `window`. Load it once per page, serve relaybell's `sw.js` from your own domain, and call the SDK from your components when you need it. Your **Project ID** is on the dashboard's **Install** page.

## Vue 3 with Vite {#vue}

Paste the [Quickstart](https://relaybell.com/docs/quickstart) snippet into `index.html` (at the project root), just before `</body>`:

```html
<script src="https://relaybell.com/relaybell-sdk.js"></script>
<script>
  RelayBell.init({ projectId: "YOUR_PROJECT_ID", apiBase: "https://relaybell.com" });
</script>
```

The page loads it once and Vue Router changes pages without reloading it, so `init()` runs once per page load, as it should (a second call starts again from its own options). Your app's code runs after this script, so `window.RelayBell` is there when your components mount.

Then download [sw.js](https://relaybell.com/sw.js) and save it as `public/sw.js`: Vite serves `public/` from the root of your site, so it opens at `https://yoursite.com/sw.js`, the default `swPath`.

## Nuxt 3 {#nuxt}

Nuxt renders on the server, so load the SDK through the page head and let it start itself. Set the options in `window.relaybellInit` before the script: the SDK starts with them once it loads (on the page's `load` event, or straight away if that has passed).

```ts
// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        { innerHTML: 'window.relaybellInit = { projectId: "YOUR_PROJECT_ID", apiBase: "https://relaybell.com" };' },
        { src: "https://relaybell.com/relaybell-sdk.js", defer: true },
      ],
    },
  },
});
```

Save [sw.js](https://relaybell.com/sw.js) as `public/sw.js` (Nuxt 3 serves `public/` from the root). Call the SDK only in code that runs in the browser: `onMounted()`, event handlers, or a plugin named `*.client.ts`.

> **Note:** Already have a service worker at `/sw.js` (a PWA module, for example)? Keep it: save relaybell's as `public/relaybell/sw.js` and add `swPath: "/relaybell/sw.js", swScope: "/relaybell/"` to the options. See [Installing the SDK](https://relaybell.com/docs/install).

## A subscribe button (optional) {#subscribe-button}

relaybell shows your opt-in prompt by itself ([Opt-in prompts](https://relaybell.com/docs/prompts)). For your own button, call `subscribeNow()` from the click — browsers only show the permission prompt in answer to one:

```vue
<script setup>
import { onMounted, ref } from "vue";

const on = ref(false);

onMounted(async () => {
  on.value = (await window.RelayBell?.isSubscribed()) === true;
});

async function subscribe() {
  const result = await window.RelayBell?.subscribeNow();
  if (result?.subscribed) on.value = true;
}
</script>

<template>
  <p v-if="on">Notifications are on.</p>
  <button v-else type="button" @click="subscribe">Turn on notifications</button>
</template>
```

In Nuxt the SDK loads with `defer` and starts on the page's `load` event, so on the very first page it may not be there yet when `onMounted()` runs: the button then shows even for a subscribed browser. A click still works, since `subscribeNow()` waits for the SDK to finish starting.

## Link signed-in users {#identify}

To [send to one of your users](https://relaybell.com/docs/send-to-one-user) by your own user id, call `identify()` once you know who is signed in. With [identity verification](https://relaybell.com/docs/identity), pass the `userHash` your server made for that user (the HMAC-SHA256 of the id, keyed with your website's secret key); never put the secret key in the app.

```js
// after sign-in, in browser code; user comes from your own auth code
window.RelayBell?.identify(user.id, user.relaybellHash);
```

Calling it again with the same values does nothing. If you know the user when the page is rendered, you can put `externalId` and `userHash` into the `init()` options (or `window.relaybellInit`) instead. On a shared browser, call `window.RelayBell?.unsubscribe()` when someone signs out of a site without identity verification — see `identify()` under [Methods](https://relaybell.com/docs/browser-sdk#sdk-methods).

## Track events {#events}

`track()` feeds your [automations](https://relaybell.com/docs/events-automation) and conversion reports:

```js
window.RelayBell?.track("cart.updated", { productId: "1234", product: "Blue Shoes", price: 49, url: location.href });
```

Or add `autoCapture: true` to the options: page views (including Vue Router page changes) and clicks on elements with `data-rb-event` are recorded for you. See [Auto-capture](https://relaybell.com/docs/events-automation#auto-capture).
