Skip to main content
Documentation

SvelteKit Installation Guide

Last researched 2026-08-26

This is the canonical Virent installation guide for SvelteKit applications.

Install with your coding agent

Hand the install to Claude Code, Codex, Cursor or another coding agent. It reads this guide, asks for your keys and keeps the server secret out of client code.

Prompt
Install Virent analytics on my SvelteKit website.

Read the canonical guide first and follow its code exactly:
https://virent.app/docs/installation/sveltekit.md

Install two separate things:
1. Human Analytics (browser): Install the browser script in SvelteKit's app.html template.
2. AI Crawler Analytics (server): Add crawler tracking to the SvelteKit server handle hook.

Values I will give you (they are in Virent under Settings > Connection):
- Publishable browser key, starts with vha_pk_. Safe in browser code.
- Site ID (VIRENT_SITE_ID) and server secret (VIRENT_INGEST_SECRET, starts with vha_sk_). Server only.
Ask me for them. Never invent a key or use a masked preview such as vha_sk_••••.

Human Analytics best practices:
- Add the script to src/app.html so it is present for every rendered document.
- Keep the tag in the head near %sveltekit.head% and keep the publishable key unchanged.
- Use the npm SDK from client modules for custom events only.

AI Crawler Analytics best practices:
- Use src/hooks.server.ts and the handle hook to observe every SvelteKit request.
- Resolve the response before sending crawler telemetry so the final status code can be included.
- Keep the server secret in private environment variables.

Rules:
- Keep VIRENT_INGEST_SECRET in server environment variables. Never put it in client code, public env variables (NEXT_PUBLIC_, VITE_, PUBLIC_) or committed files.
- Exclude API routes, static assets, images, favicons, robots.txt and sitemaps from crawler capture.
- If the site sends a Content-Security-Policy, allow https://app.virent.app in script-src and connect-src.
- Do not change unrelated code.

When you finish, list the files you changed and how I verify each path:
- Start or preview the SvelteKit app.
- Inspect the rendered head and Network requests.
- Refresh Human Analytics status.
- Run the SvelteKit app with a server adapter.
- Request a route with curl -A "GPTBot".
- Refresh AI Analytics status.

Research Notes#

  • src/app.html is the global HTML template for SvelteKit.
  • src/hooks.server.ts can define a handle hook that runs for server requests.
  • Server hooks do not run for prerendered pages or static assets, even on server deployments.

Primary references:

Human Analytics#

Add the script to src/app.html.

HTML
<!doctype html><html lang="en">  <head>    <meta charset="utf-8" />    <meta name="viewport" content="width=device-width, initial-scale=1" />    <script      defer      data-write-key="vha_pk_..."      src="https://app.virent.app/js/script.js"    ></script>    %sveltekit.head%  </head>  <body data-sveltekit-preload-data="hover">    <div style="display: contents">%sveltekit.body%</div>  </body></html>

Best practices:

  • Keep the script near %sveltekit.head%.
  • Use public env values only for publishable browser keys.
  • Use the npm SDK from client modules only when custom events are needed.

Verification:

  • Start or preview the app.
  • Inspect the rendered head and Network requests.
  • Refresh Human Analytics status.

Troubleshooting:

  • If the script is absent, confirm src/app.html is the active template.
  • If CSP blocks Virent, update deployed CSP headers.
  • If localhost testing is needed, temporarily add data-allow-localhost="true".

Ad blockers and privacy tools often block third-party analytics hosts, which silently drops visits. Relay the script and events through your own domain under /_vr, and load the script with data-api="/_vr" so events go to your domain too. The relay must forward the visitor's IP and country in x-virent-forwarded-for and x-virent-forwarded-country, with your website's secret key (vha_sk_…) in x-virent-proxy-key; without the key Virent ignores the forwarded headers and sees your server's network instead of your visitors'. Relay only /_vr/js/script.js and /_vr/v1/*, and never forward cookies or authorization headers. See First-party analytics for how Virent trusts these headers.

With nginx, add these locations to the site's server block:

TXT
location = /_vr/js/script.js {  proxy_pass https://app.virent.app/js/script.js;  proxy_set_header Host app.virent.app;  proxy_ssl_server_name on;  proxy_set_header Cookie "";}location /_vr/v1/ {  proxy_pass https://app.virent.app/v1/;  proxy_set_header Host app.virent.app;  proxy_ssl_server_name on;  proxy_set_header Cookie "";  proxy_set_header Authorization "";  proxy_set_header X-Virent-Forwarded-For $remote_addr;  # Your website's secret key: Virent trusts the forwarded IP only with it.  proxy_set_header X-Virent-Proxy-Key "vha_sk_...";}

On Cloudflare, deploy a Worker on the example.com/_vr/* route that copies only accept-language, content-type, origin and user-agent, sets x-virent-forwarded-for from cf-connecting-ip, x-virent-forwarded-country from request.cf.country and x-virent-proxy-key from a Worker secret holding your website's secret key, and fetches the same path without /_vr from https://app.virent.app. The dashboard install panel has the full Worker.

Then load the script from your domain:

HTML
<script  defer  data-api="/_vr"  data-write-key="vha_pk_..."  src="/_vr/js/script.js"></script>

AI Analytics#

Set private environment values:

ENV
VIRENT_SITE_ID=site_...VIRENT_INGEST_SECRET=vha_sk_...VIRENT_INGEST_ENDPOINT=https://app.virent.app/v1/ingest/bot

Add or compose a server hook:

TS
// src/hooks.server.tsimport { env } from "$env/dynamic/private"import type { Handle } from "@sveltejs/kit"const 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|Bytespider|meta-externalagent|meta-webindexer|meta-externalfetcher|Amazonbot|DuckAssistBot|YouBot/iexport const handle: Handle = async ({ event, resolve }) => {  const response = await resolve(event)  const userAgent = event.request.headers.get("user-agent") ?? ""  if (!staticAssetPattern.test(event.url.pathname) && aiCrawlerPattern.test(userAgent)) {    await fetch(env.VIRENT_INGEST_ENDPOINT, {      method: "POST",      headers: {        "Content-Type": "application/json",        "x-api-key": env.VIRENT_INGEST_SECRET,      },      body: JSON.stringify({        siteId: env.VIRENT_SITE_ID,        url: event.url.toString(),        host: event.url.host,        path: event.url.pathname,        method: event.request.method,        referer: event.request.headers.get("referer"),        userAgent,        statusCode: response.status,        requestId: crypto.randomUUID(),        timestamp: new Date().toISOString(),      }),    })  }  return response}

Best practices:

  • Compose with existing auth, i18n, or observability hooks instead of replacing them.
  • Resolve the response first so the final status code is available.
  • Use the hosting edge or reverse proxy for prerendered pages, static assets, and static-adapter deployments.

Verification:

SH
curl -A "GPTBot" https://your-domain.com/
  • Confirm the hook runs.
  • Refresh AI Analytics status.

Troubleshooting:

  • If hooks do not run, confirm the deployed adapter supports server-side requests.
  • If existing hooks break, compose handles in sequence.
  • If secrets are undefined, verify private env loading in the platform.