Identify & alias
Link events to authenticated users — at login, on every authenticated page load, and on logout. Includes the SSR pattern for already-logged-in visitors.
identify() is what turns vTilt from "anonymous traffic" into "people". An anonymous visitor browses with a generated $device_id. The first time you call identify('user-123'), the SDK emits a $identify event carrying both that anonymous id and the new user id; the backend merges the two person records and every later event is attributed to user-123.
#The three moments to identify
| Moment | What to call | Where it lives in your app |
|---|---|---|
| Page load with a logged-in user | vt.identify(currentUser.id, { … }) | Right after vt.init() on every page that has session context. This is the one most apps miss. |
| Login (or signup) | vt.identify(newUser.id, { … }) | Your login success handler, before the redirect. |
| Logout | vt.resetUser() | Your logout handler, before the redirect or page refresh. |
Calling identify() for the same user id on every page load is correct and intended. The SDK shortcuts the network call when nothing has changed.
#Pattern: identify on every authenticated page load
The server already knows whether the request belongs to a logged-in user — your session cookie / auth token tells you. The cleanest pattern is to expose the current user as a tiny global on the rendered page, then have the SDK pick it up immediately after vt.init().
#1. Render the current user into the page
Server-render a small JSON blob into the HTML before any analytics code runs. Use whatever templating language fits your stack — examples:
<!-- Plain HTML / PHP / Twig / Rails / Django -->
<script>
window.__currentUser = <?php echo json_encode(
$currentUser
? [
'id' => $currentUser->id,
'email' => $currentUser->email,
'name' => $currentUser->name,
'plan' => $currentUser->plan,
]
: null,
); ?>;
</script><!-- Vue 3 with SSR / Nuxt -->
<script>
window.__currentUser = {{ JSON.stringify(currentUser ?? null) }};
</script><!-- Next.js — pass via a server component to a small client island -->
<!-- See `pattern-nextjs` below for the recommended idiom. -->#2. Identify after init, every page load
import { vt } from '@v-tilt/browser'
vt.init('YOUR_PROJECT_TOKEN', { api_host: 'https://www.vtilt.com' })
const user = window.__currentUser
if (user && user.id) {
vt.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan,
})
}That's it. Anonymous visitors stay anonymous. Authenticated visitors are identified on their very first capture, every page load, regardless of whether a "login" happened in this tab or the cookie carried over from a previous visit.
#3. (Recommended) Identify on the server too, with the Node SDK
For events that originate on the server (background jobs, webhooks, server-side conversion events) call identify() from the Node SDK with the same user id. The browser and Node sides converge on the same person record because they share the user id.
#Pattern: login, signup and logout
Add a single line to each auth handler. These calls augment — not replace — the per-page-load identify.
// On successful login — before the redirect
async function onLoginSuccess(user) {
vt.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan,
})
vt.capture('user_logged_in', { method: 'password' })
// … redirect
}
// On signup — alias the freshly-created user with their pre-signup
// anonymous activity (otherwise the funnel before signup is orphaned).
async function onSignupSuccess(user) {
vt.alias(user.id) // keeps the anonymous device id linked to the new user
vt.identify(user.id, { email: user.email, plan: 'free' })
vt.capture('user_signed_up', { method: 'email' })
}
// On logout — clear identity so subsequent events are anonymous again.
function onLogoutSuccess() {
vt.capture('user_logged_out')
vt.resetUser()
// … redirect
}#Setting and updating user properties
Person properties (email, plan, role, country) are attached during identify() or independently via setUserProperties(). Both are merged onto the person record server-side; later calls overwrite earlier values for the same key.
vt.identify('user-123', {
email: 'user@example.com',
name: 'John Doe',
plan: 'premium',
})
vt.setUserProperties({
last_login: new Date().toISOString(),
login_count: 5,
})#Anonymous → identified merge
The first identify() call after a user authenticates is the merge. Internally:
- The SDK emits a
$identifyevent withdistinct_id = user.idand$anon_distinct_id = device_id. - The backend creates (or updates) a person record for
user.idand merges everything previously associated with the device id under that person. - All subsequent events use
distinct_id = user.id.
This means a visitor's pre-signup browsing — pageviews, autocapture events, performance metrics — is preserved on the same person once they sign up, as long as identify() runs in the same browser session before they leave.
#Opt-out, consent and reset
// Opt out of all analytics
vt.setConsent({ analytics: false })
// Opt back in
vt.setConsent({ analytics: true })
// Reset identity on logout
vt.resetUser()resetUser() clears the cached identity and generates a fresh anonymous $device_id on the next event. Use it on logout so two users sharing a device don't get cross-attributed.
#Pattern: Vue + PHP backend
For a complete worked example showing the SSR identify pattern in a Vue + PHP (Laravel/Symfony/plain) stack — including how to handle SPA route changes — see Integration guides › Vue + PHP.