vTilt
By roleFoundersKnow what to fix firstMarketersSee what happens after the clickCMOsSee what your budget really buys
By businessSaaSTurn signups into active usersEcommerceSee why shoppers abandon cartsAgenciesProve your work to clientsEnterpriseOne customer record for every team
MeasureWeb AnalyticsFind where buyers leaveSession ReplaySee why they left, not just that they didAdsKnow what paid visitors do nextIntegrationsOne snippet feeds your stack
ThinkPeopleOne profile per customerCustomer MemoryvTilt remembers every visitorSite KnowledgeAnswers and audits from your own pages
ActAsk AIAnswers from real behaviorAI ChatChat that knows the visitorEmail CampaignsEmail from real visits
PricingWhy vTiltDocs
Docs / Remix
Getting Started
OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes
Guides
Event forwardingReverse proxyRealtime dashboardSite knowledge
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
API Reference
Browser SDK
Script bundlesAutocaptureWeb VitalsSession recordingChat widgetFeature readinessRemote configurationDebug logging
Node SDK
Install & setupCapture, identify & aliasContext & shutdownGlobal properties & opt-outError tracking
MCP server
Guides
OverviewAuthenticationOAuthAgent skills (prompts)AI intelligenceSite knowledgeGoogle AdsMeta Ads
Client setup
CursorClaude DesktopVS CodeCodex

Getting Started

OverviewInstallInitializeIdentify usersTrack eventsLogout & resetVerify eventsCommon mistakes

Guides

Event forwardingReverse proxyRealtime dashboardSite knowledge
Next.jsNuxt.jsVue.jsReactReact RouterRemixGatsbySvelte / SvelteKitAstroAngularTanStack StartDocusaurus

API Reference

MCP server

On this page

On this page

  • 1. Add your environment variables
  • 2. Install & initialise
  • 3. Track navigations
  • 4. Identify users
  • 5. Server-side events
  • Next steps
DocsGuidesFrontend frameworksRemix

Remix

Last updated July 22, 2026

Integrate vTilt with Remix v2 — npm package or inline script, navigation pageviews, server-side events from loaders and actions.

Remix v2 is split into a server bundle and a client bundle. vTilt initialises in the browser and tracks Remix navigations explicitly. Server-side events go through the Node SDK from your loaders/actions.

Note

Remix has been folded into React Router v7. New apps should follow the React Router guide.

#1. Add your environment variables

# .env
VITE_VTILT_TOKEN=YOUR_PROJECT_TOKEN
VITE_VTILT_HOST=https://www.vtilt.com
VTILT_TRACKER_TOKEN=YOUR_PROJECT_TOKEN
text

#2. Install & initialise

Choose how you load the Browser SDK. npm initialises the SDK in entry.client.tsx; the inline script drops a stub in root.tsx with no package to install. Both expose the same window.vt global — the rest of this guide is identical either way.

Install the package:

npm install @v-tilt/browser
bash

Initialise in entry.client.tsx:

// app/entry.client.tsx
import { RemixBrowser } from '@remix-run/react'
import { startTransition, StrictMode } from 'react'
import { hydrateRoot } from 'react-dom/client'
import { vt } from '@v-tilt/browser'

vt.init(import.meta.env.VITE_VTILT_TOKEN, {
  api_host: import.meta.env.VITE_VTILT_HOST,
  autocapture: true,
  capture_pageview: false,
  capture_pageleave: true,
})

startTransition(() => {
  hydrateRoot(
    document,
    <StrictMode>
      <RemixBrowser />
    </StrictMode>,
  )
})
tsx

Tip

Need chat on first paint? Swap array.js for array.chat.js and add chat: { enabled: true }. See Script bundles.

#3. Track navigations

Remix uses client-side navigation between routes. Hook useLocation in your root and emit a pageview on every change.

// app/root.tsx
import { useEffect } from 'react'
import { useLocation } from '@remix-run/react'
import { vt } from '@v-tilt/browser'

function VTiltPageviews() {
  const { pathname, search } = useLocation()
  useEffect(() => {
    vt.capture('$pageview', {
      $current_url: window.location.href,
      $pathname: pathname + search,
    })
  }, [pathname, search])
  return null
}
tsx

Render <VTiltPageviews /> inside <body>. Using the inline script? Replace vt with window.vt and drop the import.

#4. Identify users

Use a loader to expose the current user. The component identifies on every render where user is set — covering both fresh logins and visitors arriving with a session cookie.

// app/root.tsx (continued)
import { useLoaderData } from '@remix-run/react'

export async function loader({ request }) {
  const user = await getUserFromSession(request)
  return { user }
}

function Identify() {
  const { user } = useLoaderData<typeof loader>()
  useEffect(() => {
    if (user) vt.identify(user.id, { email: user.email })
  }, [user])
  return null
}
tsx

See Identify & alias for the full model.

#5. Server-side events

For events from loaders and actions, install the Node SDK:

npm install @v-tilt/node
bash
// app/utils/vtilt.server.ts
import { VTiltNode } from '@v-tilt/node'

declare global {
  var __vtilt: VTiltNode | undefined
}
export const vtilt =
  global.__vtilt ??
  (global.__vtilt = new VTiltNode(process.env.VTILT_TRACKER_TOKEN!, {
    host: process.env.VTILT_HOST,
  }))
typescript
// app/routes/checkout.tsx
import { vtilt } from '~/utils/vtilt.server'

export async function action({ request }) {
  const form = await request.formData()
  vtilt.capture({
    distinctId: form.get('userId') as string,
    event: 'purchase_completed',
    properties: { source: 'remix-action' },
  })
  await vtilt.shutdown()
  return redirect('/thanks')
}
typescript

#Next steps

  • React Router — the modern successor to Remix.
  • Identify & alias — anonymous → known user merge.
  • Node SDK — server-side event shapes.
  • Common mistakes — integration pitfalls to avoid before you ship.
PreviousReact RouterIntegration guidesNextGatsbyIntegration guides