vTilt
Why vTiltHow It WorksFeaturesFAQDocs
Docs / Autocapture
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
DocsBrowser SDKAutocapture

Autocapture

Capture clicks, form submits and input changes without manual instrumentation. Privacy-first defaults; full configuration.

Automatically capture clicks, form submissions, and input changes without manual instrumentation. Privacy-first by default — sensitive data is filtered and cookie consent banners are excluded from capture.

#Enable autocapture

Pass autocapture: true (or an AutocaptureConfig object) to vt.init to opt in. If you don't pass the option, autocapture is controlled by the Analytics → Autocapture toggle in your project settings — the SDK fetches that on boot via /decide and starts listening once it resolves.

vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: true,
})
typescript

Important

Important: Setting autocapture: false in vt.init is final — the dashboard toggle cannot re-enable it on that page load. Leave the option unset if you want the dashboard to control it.

You can also start and stop autocapture programmatically at any time:

vt.startAutocapture() // takes precedence over the dashboard toggle
vt.stopAutocapture() // disables capture for the rest of the session
vt.isAutocaptureActive() // → boolean
typescript

#Debugging "no $autocapture events"

If pageviews are flowing but $autocapture events are missing, call vt.getAutocaptureDiagnostics() from the browser console. It returns a structured snapshot of the enabled state, the inputs that drove the decision, and a disabledReason so you can see at a glance why nothing is being captured (waiting on /decide, dashboard toggle off, server opt-out, or vt.stopAutocapture() was called).

vt.getAutocaptureDiagnostics()
// → {
//     isEnabled: false,
//     isStarted: false,
//     disabledReason: 'config_autocapture_undefined',
//     inputs: { configAutocapture: undefined, isDisabledServerSide: false, ... }
//   }
typescript

For richer logs, pass log_level: 'info' to vt.init — the SDK emits [vTilt]:autocapture lifecycle messages (started, stopped, disabled reason) on every config change.

Why is this specific element not captured?

When clicks are coming through but a particular button or link still doesn't fire $autocapture, set log_level: 'debug' and reproduce the click. The SDK prints a one-line trace per skipped event with the rule that rejected it:

[vTilt]:autocapture skip css_selector_ignorelist_hit { tag: "button", text: "Continue", target }
[vTilt]:autocapture skip vt-no-capture class on element chain { target }
[vTilt]:autocapture skip synthetic event (isTrusted=false) { type: "click", target }
[vTilt]:autocapture captured $autocapture { tag: "BUTTON", text: "Sign up", href: "/signup" }
text

The reason at the end of every skip line is one of:

ReasonWhat it means
no_window / no_elementThe event has no DOM target (rare — usually a synthetic event).
html_element / tag_htmlClick landed on the <html> element itself, not a real interactive child.
url_allowlist_missCurrent page URL didn't match autocapture.url_allowlist.
url_ignorelist_hitCurrent page URL is in autocapture.url_ignorelist.
dom_event_not_allowedEvent type (e.g. mouseover) isn't in autocapture.dom_event_allowlist.
element_allowlist_missTag (e.g. <div>) isn't in autocapture.element_allowlist.
css_selector_allowlist_missElement doesn't match any selector in autocapture.css_selector_allowlist.
css_selector_ignorelist_hitElement matches autocapture.css_selector_ignorelist (cookie banners by default).
form_event_not_allowedForm submit event for a tag that wasn't on the allowlist.
input_event_not_allowedInput change event for a non-capturable input.
tag_not_capturableElement tag isn't in the default capturable set (e.g. <span> outside a button).

Combine that with the existing vt-no-capture and vt-sensitive skip lines to fully account for every click that doesn't make it onto the wire.

#Events captured

EventTriggerConfig
$autocaptureClicks on links, buttons, inputs and other interactive elements; form submissions; input changes.autocapture: true (default)
$rageclick3+ clicks within 30 px in under 1 second.rageclick: true
$copy_autocaptureCopy/cut text events.autocapture.capture_copied_text: true
$scroll_depthUser crosses a scroll-depth milestone on the current page.autocapture.scroll_depth.milestones

$pageleave can include $prev_pageview_scroll_depth_pct when autocapture.scroll_depth.pageleave is enabled (requires capture_pageleave).

#Scroll depth

Two independently opt-in modes under autocapture.scroll_depth (both default off):

ModeConfigWhat you get
Milestonesmilestones: true or milestones: { thresholds: [50, 100] }$scroll_depth once per threshold per page
Page leavepageleave: trueMax scroll % on the page being left, on $pageleave
// Milestones only — funnel-friendly
vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: { scroll_depth: { milestones: true } },
})

// Page-leave summary only — lower volume
vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: { scroll_depth: { pageleave: true } },
  capture_pageleave: true,
})
typescript

A passive scroll listener runs when either mode is on. State resets on each $pageview (including SPA navigations). Milestone events respect url_allowlist and url_ignorelist like other autocapture.

#Properties collected

PropertyDescription
$event_typeDOM event type (click, submit, change).
$el_textVisible text of the element (buttons, links, nested spans).
$elements_chainEncoded chain string of element and ancestors (tag, classes, attributes, position).
$external_click_urlDestination URL when clicking an external link.
$selected_contentCopied/cut text (with capture_copied_text).
$copy_typeClipboard event type (copy or cut).
$el_valueForm field value on change (with capture_element_values, privacy-filtered).
$selected_textHuman-readable selection text — dropdown option text, checkbox label.
$element_selectorsMatched CSS selectors from action definitions.
$scroll_depth_pctMilestone threshold reached (milestones mode).
$scroll_depth_max_pctMax scroll depth % when the milestone fired.
$prev_pageview_scroll_depth_pctMax scroll depth % on the page being left (pageleave mode, on $pageleave).

#Advanced configuration

Pass an object to autocapture for fine-grained control over which pages, elements, and events are captured.

vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: {
    url_allowlist: ['https://example.com/app/.*'],
    url_ignorelist: ['https://example.com/admin/.*'],
    dom_event_allowlist: ['click', 'submit'],
    element_allowlist: ['a', 'button', 'form'],
    css_selector_allowlist: ['[data-track]', '.trackable'],
    css_selector_ignorelist: ['[data-no-track]', '#admin-panel'],
    element_attribute_ignorelist: ['data-secret', 'aria-label'],
    capture_copied_text: true,
    capture_element_values: true,
  },
})
typescript
OptionTypeDescription
url_allowlist(string | RegExp)[]Only capture on pages matching these URLs/patterns.
url_ignorelist(string | RegExp)[]Skip capture on pages matching these URLs (overrides allowlist).
dom_event_allowliststring[]Limit to specific event types (click, change, submit).
element_allowliststring[]Limit to HTML tags (a, button, form, input, select, textarea, label).
css_selector_allowliststring[]Only capture elements matching these CSS selectors.
css_selector_ignoreliststring[]Exclude elements matching these selectors (defaults exclude cookie banners).
element_attribute_ignoreliststring[]Exclude specific HTML attributes from captured properties.
capture_copied_textbooleanCapture text on copy/cut events as $copy_autocapture.
capture_element_valuesbooleanCapture form field values on change (opt-in, default false).
scroll_depth.milestonesboolean | { thresholds?: number[] }Milestone $scroll_depth events (default off).
scroll_depth.pageleavebooleanMax scroll % on $pageleave (default off).
scroll_depth.scroll_root_selectorstring | string[]Scroll container; defaults to window.

#Privacy controls

Use CSS classes to control what gets captured. Privacy protections apply automatically — password fields, sensitive names (cc, ssn, etc.), and credit card / SSN patterns in values are never captured.

<!-- Prevent autocapture on this element and its children -->
<div class="vt-no-capture">
  <button>This click will NOT be captured</button>
</div>

<!-- Mark element as containing sensitive data -->
<div class="vt-sensitive">
  <input type="text" placeholder="SSN" />
</div>

<!-- Force capture on a specific element -->
<span class="vt-include">This text WILL be captured</span>
html
ClassEffect
vt-no-capturePrevent capture on this element and all children.
vt-sensitiveMark as sensitive; only tag name, id, and class are captured (no text or input values).
vt-includeForce capture on an element that would otherwise be excluded.

Note

Note: By default, autocapture excludes clicks on common cookie consent banners (CookieBot, OneTrust, Klaro, CookieYes, and many others). To disable this filtering, set css_selector_ignorelist: [].

#Custom properties

Attach custom key-value properties to autocapture events using data-vt-capture-attribute-* HTML attributes.

<button
  data-vt-capture-attribute-feature="signup"
  data-vt-capture-attribute-variant="blue"
>
  Sign Up
</button>

<!-- The autocapture event will include:
  { feature: "signup", variant: "blue", ...standard props } -->
html

#Rage click detection

Detect frustrated users clicking repeatedly in the same area. Fires a separate $rageclick event.

vt.init('YOUR_PROJECT_TOKEN', {
  api_host: 'https://www.vtilt.com',
  autocapture: true,
  rageclick: true,
})

// When a user clicks 3+ times within 30px
// in under 1 second, a $rageclick event fires.
typescript
PreviousEvent trackingBrowser SDKNextIdentify & aliasBrowser SDK

On this page

  • Enable autocapture
  • Debugging "no $autocapture events"
  • Events captured
  • Scroll depth
  • Properties collected
  • Advanced configuration
  • Privacy controls
  • Custom properties
  • Rage click detection