vTilt
Why vTiltHow It WorksFeaturesFAQDocs
Docs / Install
Quick startEvent forwarding
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceGoogle Ads
Client setup
CursorClaude DesktopVS CodeCodex
Realtime
Debug ViewRealtime Dashboard
Integration guides
Frontend frameworks
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus
Backend frameworks
NestJSHonoCloudflare WorkersDjangoFlaskLaravelPhoenixRuby on Rails
Backend languages
PythonPHPRubyElixirGoJava.NET / C#Rust
Stack guides
Vue + PHP
SDK
Browser SDK
InstallScript bundlesEvent trackingAutocaptureIdentify & aliasWeb VitalsSession recordingChat widgetFeature readinessRemote configurationReverse proxyDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdown

Documentation

vTilt
Quick startEvent forwarding

MCP server

Realtime

Debug ViewRealtime Dashboard

Integration guides

SDK

InstallScript bundlesEvent trackingAutocaptureIdentify & aliasWeb VitalsSession recordingChat widgetFeature readinessRemote configurationReverse proxyDebug logging
DocsIntegration guidesInstall

Install

Add the @v-tilt/browser SDK via npm/pnpm or a script tag in your HTML.

Add the Browser SDK via npm/pnpm or a script tag in your HTML. Both options expose the same window.vt global once init() runs.

#Package manager

npm install @v-tilt/browser
bash

#Script tag vs npm

Install pathStub needed?Notes
npm — import { vt } from '@v-tilt/browser'Novt is the real SDK. All methods (updateConfig, sendChatMessage, …) exist as soon as your module loads.
Script tag — inline loader + array.jsYesThe loader registers a fixed list of method names on window.vt so calls made before array.js finishes loading queue in vt._i and replay on init.

If you use the script tag and call APIs early (hero CTA → vt.sendChatMessage(), route hook → vt.updateConfig(…)), ensure your snippet includes every method you use. The dashboard Integration Script and Web snippet copy the current list automatically. The canonical set is VTILT_SNIPPET_STUB_METHOD_NAMES in @v-tilt/browser.

#Script tag

For sites without a build step, paste the snippet into your HTML <head>. The snippet creates a stub at window.vt, queues every call, and replaces itself with the full SDK as soon as array.js loads.

<!-- Add to your HTML <head> -->
<script>
  !(function (t, e) {
    var o, n, p, r
    function g(t, e) {
      var o = e.split('.')
      ;(2 == o.length && ((t = t[o[0]]), (e = o[1])),
        (t[e] = function () {
          t.push([e].concat(Array.prototype.slice.call(arguments, 0)))
        }))
    }
    if (!e.__SV && !(window.vt && window.vt.__loaded)) {
      ;((window.vt = e),
        (e._i = []),
        (o =
          'init capture identify setUserProperties resetUser getUserIdentity getDeviceId getUserState alias getConfig getSessionId updateConfig setConsent on once off startAutocapture stopAutocapture startSessionRecording stopSessionRecording openChat closeChat toggleChat showChat hideChat sendChatMessage gtag setGoogleUserData'.split(
            ' ',
          )))
      for (n = 0; n < o.length; n++) g(e, o[n])
      ;((e.init = function (i, s, a) {
        ;(((p = t.createElement('script')).type = 'text/javascript'),
          (p.crossOrigin = 'anonymous'),
          (p.async = !0),
          (p.src = s.script_host
            ? s.script_host + '/array.js'
            : s.api_host + '/dist/array.js'),
          (p.onerror = function () {
            console.error('vTilt: Failed to load library script:', p.src)
          }),
          (r = t.getElementsByTagName('script')[0]).parentNode.insertBefore(
            p,
            r,
          ))
        var u = e
        for (
          void 0 !== a ? (u = e[a] = []) : (a = 'vt'),
            u.toString = function (t) {
              var e = 'vt'
              return ('vt' !== a && (e += '.' + a), t || (e += ' (stub)'), e)
            },
            n = 0;
          n < o.length;
          n++
        )
          g(u, o[n])
        e._i.push([i, s, a])
      }),
        (e.__SV = 1))
    }
  })(document, window.vt || [])
  vt.init('YOUR_PROJECT_TOKEN', {
    api_host: 'https://www.vtilt.com',
  })
</script>
html

#Initialise

Call vt.init() once with your project token and config. Any other SDK call before init() is a no-op; calls after are queued and applied in order.

import { vt } from '@v-tilt/browser'

vt.init('YOUR_PROJECT_TOKEN', {
  // Optional. https://www.vtilt.com is the managed-cloud ingest host;
  // self-hosted or reverse-proxy users replace this with their own origin.
  api_host: 'https://www.vtilt.com',
  autocapture: true,
  capture_pageview: true,
  capture_pageleave: true,
  persistence: 'localStorage',
})
typescript

Note

Note: Only the project token is required. api_host is optional: most integrators on managed cloud use https://www.vtilt.com as above. If you self-host or front vTilt with a reverse proxy, point it at your own origin. Omit it altogether to send events to the current page origin via relative URLs (handy when your proxy and your site share an origin).

The default snippet loads array.js (core only). Optional features such as chat and session recording lazy-load separate files. See Script bundles to pick array.chat.js, array.full.js, or an npm import variant.

#Update config at runtime

vt.updateConfig(patch) is the single API for changing SDK settings after init(). Pass any subset of the same options you use in init() — autocapture, persistence, chat, and so on. Top-level keys are shallow-merged into the stored config; nested chat is deep-merged so a partial chat patch does not remove other chat fields.

// Toggle autocapture without re-init
vt.updateConfig({ autocapture: false })

// Adjust chat bubble inset on a route change (see [Chat](/docs/browser/chat#bubble-position-and-offset))
vt.updateConfig({
  chat: {
    bubble: { offset: { bottom: 88, right: 24 } },
  },
})

// Combine unrelated changes in one call
vt.updateConfig({
  capture_pageview: false,
  chat: { greeting: 'Welcome back!' },
})
typescript

Registered features (chat, session recording, autocapture, …) receive the merged config through the same notification path. An empty patch vt.updateConfig({}) is valid — for example it can re-trigger remote config in a long-lived tab.

#Recommended init options

OptionRequiredTypical valueWhy you might change it
autocapturenotrueDisable for highly custom event tracking; pass an object to scope what's captured.
capture_pageviewnotrueDisable when you handle pageviews manually (e.g. exotic SPA frameworks).
capture_pageleavenotrue$pageleave events power engagement metrics — keep on unless you have a reason to drop them.
persistencenolocalStorageUse cookie if you need cross-subdomain identity, or memory to disable client-side persistence entirely.
api_hostnohttps://www.vtilt.comManaged-cloud ingest host. Replace with your own origin if you reverse-proxy or self-host vTilt; omit to fall back to relative URLs (same-origin).
PreviousVue + PHPIntegration guidesNextScript bundlesBrowser SDK

On this page

  • Package manager
  • Script tag vs npm
  • Script tag
  • Initialise
  • Update config at runtime
  • Recommended init options