Overview
The Tinybird tracker automatically captures page views when visitors navigate your website. It works seamlessly with:
- Traditional multi-page websites - Tracks each page load
- Single Page Applications (SPAs) - Tracks client-side navigation
- Hash-based routing - Monitors hash changes
- History API - Captures pushState and popState events
Automatic Tracking
Once the tracker script is installed, page view tracking starts automatically. No additional code is required.
<script
defer
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
></script>
How It Works
Initial Page Load
When a visitor first lands on your website, the tracker:
- Generates or retrieves a session ID
- Collects page metadata
- Sends a
page_hit event to Tinybird
SPA Navigation
For single-page applications, the tracker monitors:
Hash Changes
History API
Prerendering
// Automatically tracked
window.addEventListener('hashchange', _trackPageHit)
When the URL hash changes (e.g., #/home to #/about), a new page view is recorded.// Automatically tracked
const originalPushState = history.pushState
history.pushState = function () {
originalPushState.apply(this, arguments)
_trackPageHit()
}
window.addEventListener('popstate', _trackPageHit)
When using history.pushState() or the back/forward buttons, page views are captured.// Handles prerendered pages
if (document.visibilityState === 'prerender') {
document.addEventListener('visibilitychange', handleVisibilityChange)
}
For prerendered pages, tracking waits until the page becomes visible.
Event Data Structure
Each page_hit event includes the following data:
ISO 8601 timestamp of when the event occurred.Example: "2024-03-11T14:23:45.123Z"
Event type, always "page_hit" for page views.
Unique identifier for the user session (UUID v4).Example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
Tracker version, currently "1".
Tenant identifier (if configured with data-tenant-id).
Domain identifier (if configured with data-domain).
Contains detailed page information:
Browser user agent string.
Browser language/locale (e.g., "en-US").
Country code derived from timezone (e.g., "US").
Current page path (e.g., "/products/analytics").
Example Event
{
"timestamp": "2024-03-11T14:23:45.123Z",
"action": "page_hit",
"version": "1",
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"tenant_id": "my-tenant",
"domain": "example.com",
"payload": {
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
"locale": "en-US",
"location": "US",
"referrer": "https://google.com",
"pathname": "/products/analytics",
"href": "https://example.com/products/analytics"
}
}
Session Management
The tracker maintains user sessions with a 30-minute timeout.
Session ID Generation
// From middleware/src/index.js
function _uuidv4() {
return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
(
c ^
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
).toString(16)
)
}
Storage Options
Sessions can be stored using different methods:
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TOKEN"
data-storage="cookie"
></script>
Cookie settings:
- Max-Age: 1800 seconds (30 minutes)
- Path:
/
- Secure:
true
- Domain: Uses
data-domain if specified
Privacy Features
Bot Detection
The tracker automatically filters out requests from:
// From middleware/src/index.js
if (window.__nightmare || window.navigator.webdriver || window.Cypress)
return
- Testing frameworks (Nightmare, Selenium, Cypress)
- Automated browsers
Data Validation
The tracker validates data before sending:
function _isValidUserAgent(userAgent) {
if (!userAgent || typeof userAgent !== 'string') {
return true
}
if (userAgent.length > 500) {
return false
}
return true
}
User agent strings longer than 500 characters are rejected.function _isValidPayload(payloadStr) {
if (!payloadStr || typeof payloadStr !== 'string') {
return false
}
if (payloadStr.length < 2 || payloadStr.length > 10240) {
return false
}
return true
}
Payload must be between 2 and 10,240 bytes.
PII Masking
Sensitive attributes are automatically masked:
const attributesToMask = [
'username', 'user', 'user_id', 'userid',
'password', 'pass', 'pin', 'passcode',
'token', 'api_token', 'email', 'address',
'phone', 'sex', 'gender', 'order', 'order_id',
'orderid', 'payment', 'credit_card',
]
These fields are replaced with "********" before sending.
Location Detection
The tracker derives country from the browser’s timezone:
// From middleware/src/index.js
function getCountryAndLocale() {
let country, locale;
try {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
country = timezones[timezone]; // Maps timezone to country code
locale = navigator.languages && navigator.languages.length
? navigator.languages[0]
: navigator.language
} catch (error) {
// ignore error
}
return { country, locale };
}
This approach is privacy-friendly as it doesn’t require IP geolocation or third-party services.
Timing
Page hits include a 300ms delay to accommodate SPA routers:
// Wait for SPA routers to update the URL
setTimeout(() => {
_sendEvent('page_hit', {
'user-agent': window.navigator.userAgent,
locale,
location: country,
referrer: document.referrer,
pathname: window.location.pathname,
href: window.location.href,
})
}, 300)
Next Steps
Custom Events
Track custom user interactions
Web Vitals
Monitor performance metrics