# Nuxt (SSR)

Since Nuxt renders pages on the server first, the tiun SDK must only initialize on the client. The recommended approach is to set up everything in your `app.vue` using `onMounted`.

For plain Vue 3 (no SSR), see the [Vue example](/sdk/examples/vue.md).

***

## Initialize

Initialize tiun inside `onMounted` in your `app.vue`. The SDK is idempotent — calling `init` again on an initialized instance merges config rather than re-initializing — so no guard is needed.

```vue
<!-- app.vue -->
<script setup>
import { tiun } from '@tiun/sdk';
import { onMounted } from 'vue';

onMounted(() => {
  tiun.init({
    snippetId: 'YOUR_SNIPPET_ID',
    language: 'en', // 'en' | 'de' | 'fr'
  });
});
</script>

<template>
  <NuxtPage />
</template>
```

{% hint style="info" %}
You don't need to call `tiun.destroy()` from `app.vue` — the root only unmounts when the page is gone. See [Lifecycle and `destroy()`](/sdk/getting-started/initialization.md#lifecycle-and-destroy) for the full matrix.
{% endhint %}

***

## Checkout

Trigger checkout from any component. The SDK is already initialized in `app.vue`, so you can call methods directly.

```vue
<script setup>
import { tiun } from '@tiun/sdk';
</script>

<template>
  <div>
    <button @click="tiun.checkout({ productId: 'p-live-light' })">
      Light — EUR 19/mo
    </button>
    <button @click="tiun.checkout({ productId: 'p-live-pro' })">
      Pro — EUR 199/mo
    </button>
  </div>
</template>
```

***

## Shared state with `useState`

Use Nuxt's `useState` to create reactive state that any component can read. This is SSR-safe and works across the app without prop drilling.

```vue
<!-- app.vue -->
<script setup>
import { tiun } from '@tiun/sdk';
import { onMounted } from 'vue';

const isAuthenticated = useState('isAuthenticated', () => false);
const user = useState('user', () => null);

onMounted(() => {
  tiun.on('userChange', (data) => {
    isAuthenticated.value = data.isAuthenticated;
    user.value = data.user;
  });

  tiun.init({
    snippetId: 'YOUR_SNIPPET_ID',
    language: 'en',
  });
});
</script>
```

Any component can then read the shared state:

```vue
<script setup>
const isAuthenticated = useState('isAuthenticated');
const user = useState('user');
</script>

<template>
  <p v-if="isAuthenticated">Logged in as {{ user.email }}</p>
  <p v-else>Not logged in</p>
</template>
```

***

## Gate content

Use `useState` values in any page or component to show or hide content based on `productAccess`.

```vue
<script setup>
import { tiun } from '@tiun/sdk';

const isAuthenticated = useState('isAuthenticated');
const user = useState('user');
</script>

<template>
  <div v-if="!isAuthenticated">
    <p>Please log in or subscribe.</p>
  </div>

  <div v-else-if="user?.productAccess?.includes('p-live-pro')">
    <!-- Pro content -->
  </div>

  <div v-else>
    <button @click="tiun.checkout({ productId: 'p-live-pro' })">
      Upgrade to Pro
    </button>
  </div>
</template>
```

***

## Paywall events (time-based)

For time-based billing, use `paywallShow` and `paywallHide` to control access. Wire them to shared state in `app.vue`.

```vue
<!-- app.vue -->
<script setup>
import { tiun } from '@tiun/sdk';
import { onMounted } from 'vue';

const showPaywall = useState('showPaywall', () => true);

onMounted(() => {
  tiun.on('paywallShow', () => {
    showPaywall.value = true;
  });

  tiun.on('paywallHide', () => {
    showPaywall.value = false;
  });

  tiun.init({
    snippetId: 'YOUR_SNIPPET_ID',
    language: 'en',
  });
});
</script>
```

Then in any component:

```vue
<script setup>
import { tiun } from '@tiun/sdk';

const showPaywall = useState('showPaywall');
</script>

<template>
  <div v-if="showPaywall">
    <h2>Premium content</h2>
    <button @click="tiun.start()">
      Get access
    </button>
  </div>

  <article v-else>
    <!-- premium content -->
  </article>
</template>
```

***

## Content management (time-based)

Track route changes to keep billing accurate. Use `router.afterEach` with an `import.meta.client` guard so it only runs in the browser. Send the initial content state when the SDK is ready.

```vue
<!-- app.vue -->
<script setup>
import { tiun } from '@tiun/sdk';
import { onMounted } from 'vue';

const router = useRouter();

onMounted(() => {
  tiun.on('ready', () => {
    sendContentState(router.currentRoute.value);
  });

  tiun.init({
    snippetId: 'YOUR_SNIPPET_ID',
    language: 'en',
  });
});

function sendContentState(route) {
  const isPaid = route.path !== '/';
  tiun.setContent({
    type: isPaid ? 'active' : 'inactive',
    contentId: route.params?.slug || route.path
  });
}

if (import.meta.client) {
  router.afterEach((to) => {
    if (!tiun.isReady) return;
    sendContentState(to);
  });
}
</script>
```

***

## Alternative: plugin approach

Instead of `app.vue`, you can initialize tiun in a Nuxt plugin. The `.client.ts` suffix ensures it only runs in the browser.

```ts
// plugins/tiun.client.ts
import { tiun } from '@tiun/sdk';

export default defineNuxtPlugin(() => {
  tiun.init({
    snippetId: 'YOUR_SNIPPET_ID',
    language: 'en',
  });
});
```

This runs before any component mounts. Either approach works — pick whichever fits your project layout. Neither needs an explicit teardown at the app root.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tiun.io/sdk/examples/nuxt.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
