Vue and Nuxt placement
There is no separate framework package. Load the SDK once, paused until consent, through the page-owned bridge from the Consent guide (/telltale-consent.js, a file you save on your own origin from that guide, for example in public/), connect your CMP recipe to it, and reconnect identity from one persistent root component. A file with the .client suffix runs only in the browser, so the SDK never touches server rendering.
The bridge creates the client with waitForConsent: true. Your CMP recipe calls applyConsent(true), which runs client.optIn(), or applyConsent(false), which runs client.optOut(); an unknown choice calls nothing.
Vue single-page application
Place the bridge, then your CMP recipe, before the application entry in index.html:
<!-- index.html -->
<script src="/telltale-consent.js"></script>
<script src="/telltale-cookiebot.js" data-category="statistics"></script>
<script type="module" src="/src/main.js"></script>Nuxt
Serve the files from public/ and add them as classic head scripts. A classic script in the head runs before the client plugins.
// nuxt.config.ts
export default defineNuxtConfig({
app: {
head: {
script: [
{ src: '/telltale-consent.js' },
{ src: '/telltale-cookiebot.js', 'data-category': 'statistics' }
]
}
}
});Nuxt has no built-in Content Security Policy. If you send a nonce-based policy, propagate the nonce to these tags with your CSP tooling (for example the third-party nuxt-security module), or use the exact host allowlist from the CSP guide. Never add unsafe-inline to make it work. Keep a Sourcepoint loader after the bridge so its actions can call applyConsent.
To expose the client to components, a client-only plugin can provide the bridge's promise. Do not create a second bridge or client in it. The file goes in the plugins directory of your source directory: app/plugins/telltale.client.ts in Nuxt 4 (whose srcDir defaults to app/), or plugins/telltale.client.ts in Nuxt 3 and in Nuxt 4 projects that kept the Nuxt 3 layout. The .client suffix keeps it out of server rendering. The code is the same in both:
// Nuxt 4: app/plugins/telltale.client.ts · Nuxt 3: plugins/telltale.client.ts
export default defineNuxtPlugin(() => {
const bridge = window.telltaleConsent;
return { provide: { telltale: bridge ? bridge.ready : Promise.reject(new Error('Telltale bridge unavailable')) } };
});Reconnect identity on consent changes
In the persistent root component that owns authentication (App.vue, or app.vue in Nuxt: app/app.vue in Nuxt 4), connect identity once with connectIdentity (from the browser adapter at https://app.trytelltale.dev/adapters/v1/browser.js, saved as lib/telltale/browser.js; Server adapters lists its SHA-256). It invalidates pending auth reads on withdrawal and reads current authentication again after every grant:
<script setup>
import { onMounted, onBeforeUnmount } from 'vue';
import { connectIdentity } from './lib/telltale/browser.js';
import { readCurrentIdentity, onAuthChange } from './lib/auth';
let disposed = false;
let identity;
onMounted(() => {
const bridge = window.telltaleConsent;
if (!bridge) return; // blocked by CSP, an extension or the network: stay inert
bridge.ready.then(client => {
if (disposed) return;
identity = connectIdentity(client, {
readIdentity: ({ signal }) => readCurrentIdentity({ signal }), // {userId, accountId} or null
subscribe: onAuthChange // login, logout and account switch; returns an unsubscribe function
});
}).catch(() => {});
});
// Removes only this subscription and cancels stale auth reads; it never withdraws consent.
onBeforeUnmount(() => {
disposed = true;
identity?.dispose();
});
</script>Browser access happens only after mounting. Keep this component above route content so navigation reuses the subscription. Logging in never grants consent, and connectIdentity never calls optIn(). With Better Auth, use connectBetterAuth from the Authentication guide instead.
Protected actions
Get the client with await window.telltaleConsent.ready (or useNuxtApp().$telltale), call await client.getSessionToken() just before the protected request and send it to your own server route. Verify it there with POST /api/v1/verify using the Site server key from private runtime configuration (runtimeConfig, not runtimeConfig.public).