Nastardocs v1 · API v1

Last updated 2026-09-22 · In production on this site

Guide: Nuxt

Integrating Nastar into a Nuxt app, including the one thing a static site doesn't need: reporting client-side route changes.

This is the integration nastar.dev itself runs — the snippets below are the real ones from this site's nuxt.config.ts and its page-view plugin, not a sketch. The SDK has no Nuxt-specific API and no Vue dependency; what makes this a guide rather than a copy of plain HTML is where each piece goes in a Nuxt app.

1. Add the SDK

Nuxt owns the document head, so the install snippet goes in nuxt.config.ts rather than an HTML file:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      script: [
        {
          src: 'https://cdn.nastar.dev/sdk/v1/nastar.min.js',
          defer: true,
          'data-site-key': 'YOUR_PUBLIC_SITE_KEY',
        },
      ],
    },
  },
})

That auto-init build reads data-site-key off its own tag and exposes the client as window.Nastar. The public site key is an identifier, not a credential — committing it is fine, which is why it belongs here and not in runtimeConfig. Nuxt emits head.script entries into <head>, where a deferred script executes before DOMContentLoaded — early enough that the banner isn't waiting on your app bundle to hydrate.

If you bundle the SDK instead, import the ESM build from the CDN (https://cdn.nastar.dev/sdk/v1/nastar.esm.js) inside a .client.ts plugin and call createNastarClient there. Don't use npm install @nastar/consent-sdk — that package isn't published yet; see SDK reference.

Keep it out of dev builds

Worth doing deliberately: in local development the banner covers the viewport until someone decides, which blocks Playwright runs against the dev server, and any analytics you've enabled reports local work into production. Gate the tag on the build environment:

script: [
  ...("production" === 'production'
    ? [{
        src: 'https://cdn.nastar.dev/sdk/v1/nastar.min.js',
        defer: true,
        'data-site-key': 'YOUR_PUBLIC_SITE_KEY',
      }]
    : []),
],

If you do this, test the consent flow against a production build (nuxt build && nuxt preview) rather than nuxt dev — otherwise the thing you most need to verify is the thing you never load.

2. Gate an optional script

The SDK is browser-only: there is no server entry point, and nothing to render during SSR. Put the gating in a client-only plugin so it never runs on the server:

// app/plugins/analytics.client.ts
export default defineNuxtPlugin(() => {
  onNuxtReady(async () => {
    const nastar = window.Nastar
    if (!nastar) return

    await nastar.ready
    nastar.loadWhenConsented('analytics', () => {
      const script = document.createElement('script')
      script.src = 'https://your-analytics-vendor.example/tag.js'
      document.body.appendChild(script)
    })
  })
})

Two details worth not skipping. The .client suffix is what keeps the plugin off the server. And the onNuxtReady wrapper is what makes window.Nastar reliably present: the SDK tag is a deferred script, so it executes before DOMContentLoaded, but its ordering against Nuxt's own entry module isn't something to depend on. onNuxtReady runs after hydration, by which point the SDK has definitely initialized. Gating is not latency-sensitive — loadWhenConsented is waiting on a human decision regardless.

Never render an optional tracking script from a server component or a plain head.script entry "and hide it on the client" — the request has already left by then. If server-side tracking is genuinely required, the server has to verify consent independently; the SDK can't do it for you.

3. Add a "Privacy settings" control

Put it in your footer component so it's reachable from every page — a visitor must be able to change their mind after dismissing the banner:

<template>
  <button type="button" @click="openPreferences">Privacy settings</button>
</template>

<script setup lang="ts">
function openPreferences() {
  window.Nastar?.openPreferences()
}
</script>

The optional chaining matters: on the server, and before the SDK's script has loaded, window.Nastar is undefined. The button renders either way and does nothing until the SDK is ready, which is the correct degradation.

4. Report client-side page views

This is the step a static site doesn't have, and the one most easily missed. If you've enabled Nastar-managed Google Analytics, the SDK fires the first page view itself when GA activates, and GA4's own history-based detection is switched off so there's exactly one page-view mechanism rather than two racing to double-count. In a Nuxt app that means a route change reports nothing unless you hook it up:

// app/plugins/nastar-page-views.client.ts
declare global {
  interface Window {
    Nastar?: { trackPageView: (pagePath?: string) => void }
  }
}

export default defineNuxtPlugin(() => {
  useRouter().afterEach((to) => {
    window.Nastar?.trackPageView(to.fullPath)
  })
})

afterEach rather than beforeEach: you want the path that was actually navigated to. trackPageView no-ops until Google Analytics is enabled and loaded, so this needs no consent check of its own — in Basic consent mode that means after consent (and it stops again on withdrawal), and in Advanced mode route changes are also reported before a decision, as the cookieless pings that mode exists for. SDK reference has the full contract.

If you gated the script tag on NODE_ENV above, guard this plugin the same way (if (false) return) and keep the two in step — a page-view hook with no SDK under it is harmless, but a mismatch between them is a confusing thing to debug later.

5. Verify it

Against a production build, not the dev server:

  • The banner renders before any optional script runs — check the network tab on a fresh profile, not just the SDK's state.
  • Rejecting leaves every non-necessary category off, and it stays that way after a reload.
  • Accepting, then navigating client-side between pages, records one page view per route change — not zero, and not two.
  • A hard reload after a decision skips the banner and restores the same choice.
  • The "Privacy settings" control reopens the dialog with the current choice preselected.

CSP

A minimal policy for this pattern:

Content-Security-Policy:
  script-src 'self' https://cdn.nastar.dev https://your-analytics-vendor.example;
  connect-src 'self' https://api.nastar.dev https://cdn.nastar.dev;

Nuxt's own hydration payload may need 'unsafe-inline' or a nonce depending on your rendering mode — that's a Nuxt concern, not a Nastar one. The SDK itself needs no inline-script exception: it creates gated tags with document.createElement.

Other frameworks

Nuxt and plain HTML are the only guides that exist. Astro, Next.js and WordPress are not written and not tested — the SDK has no framework-specific API, so the shape above (load the script in the framework's head config, gate optional scripts client-side, hook the router for page views) transfers in principle, but nobody has verified it there. Treat an adaptation as your own untested work.