Nuxt Installation Guide
Last researched 2026-08-26
This is the canonical Virent installation guide for Nuxt applications.
Research Notes#
- Nuxt
useHeadmanages scripts in the document head. - Nitro server plugins can subscribe to
afterResponse, after the final status is known. - Static-only output has no request-time Nitro hook; install crawler tracking at the hosting provider instead.
Primary references:
- https://nuxt.com/docs/4.x/api/composables/use-head
- https://nuxt.com/docs/4.x/directory-structure/server
Human Analytics#
Add the browser script globally in app/app.vue.
TXT
<script setup lang="ts">useHead({ script: [ { src: "https://app.virent.app/js/script.js", defer: true, "data-write-key": "vha_pk_...", }, ],})</script><template> <NuxtLayout> <NuxtPage /> </NuxtLayout></template>Best practices:
- Keep the script global in
app/app.vue. - Use
deferso Nuxt hydration is not blocked. - Keep custom event code in client-side composables or plugins.
- Use public runtime config only for publishable values.
Verification:
- Open a Nuxt route and inspect the head.
- Navigate to another route and confirm pageview traffic.
- Refresh Human Analytics status.
Troubleshooting:
- If the script is absent, confirm the project uses
app/app.vueand theuseHeadcall is not conditional. - If CSP blocks Virent, allow the origin in
script-srcandconnect-src. - If local events are expected, temporarily add
data-allow-localhost="true".
AI Analytics#
Add private env values:
ENV
VIRENT_SITE_ID=site_...VIRENT_INGEST_SECRET=vha_sk_...VIRENT_INGEST_ENDPOINT=https://app.virent.app/v1/ingest/botCreate a Nitro server plugin:
TS
// server/plugins/virent-crawler.tsconst staticAssetPattern = /\.(?:png|jpg|jpeg|gif|svg|ico|webp|css|js|map|txt|xml)$/iconst aiCrawlerPattern = /GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|claudebot|Claude-Web|Claude-User|Claude-SearchBot|PerplexityBot|PerplexityBot\/|Perplexity-User|Perplexity-User\/|Google-CloudVertexBot|DeepSeekBot|DeepSeekSpider|CCBot|Applebot|Bytespider|meta-externalagent|meta-webindexer|meta-externalfetcher|Amazonbot|DuckAssistBot|PetalBot|YouBot/iexport default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook("afterResponse", async (event) => { const requestUrl = getRequestURL(event) const userAgent = getHeader(event, "user-agent") ?? "" if (staticAssetPattern.test(requestUrl.pathname) || !aiCrawlerPattern.test(userAgent)) { return } await $fetch(process.env.VIRENT_INGEST_ENDPOINT!, { method: "POST", headers: { "x-api-key": process.env.VIRENT_INGEST_SECRET! }, body: { siteId: process.env.VIRENT_SITE_ID!, url: requestUrl.toString(), host: requestUrl.host, path: requestUrl.pathname, method: event.method, referer: getHeader(event, "referer") ?? null, userAgent, statusCode: event.node.res.statusCode, requestId: crypto.randomUUID(), timestamp: new Date().toISOString(), }, }) })})Best practices:
- Use the Nitro
afterResponsehook so telemetry has the final response status and does not delay route handling. - Use private runtime config or server environment variables for the secret.
- Move tracking to the CDN/edge layer for static-only Nuxt output.
Verification:
SH
curl -A "GPTBot" https://your-domain.com/- Confirm the Nitro server plugin runs.
- Refresh AI Analytics status.
Troubleshooting:
- If
afterResponsedoes not run, confirm the deployment uses a server/Nitro runtime. - If the secret is undefined, check private runtime config and deployment env names.
- If events are duplicated, ensure only one middleware or edge layer sends events.