Skip to main content
Virent

Nuxt Installation Guide

Last researched 2026-08-26

This is the canonical Virent installation guide for Nuxt applications.

Research Notes#

  • Nuxt useHead manages 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 defer so 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.vue and the useHead call is not conditional.
  • If CSP blocks Virent, allow the origin in script-src and connect-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/bot

Create 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 afterResponse hook 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 afterResponse does 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.