Skip to main content

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:
  1. Generates or retrieves a session ID
  2. Collects page metadata
  3. Sends a page_hit event to Tinybird

SPA Navigation

For single-page applications, the tracker monitors:
// Automatically tracked
window.addEventListener('hashchange', _trackPageHit)
When the URL hash changes (e.g., #/home to #/about), a new page view is recorded.

Event Data Structure

Each page_hit event includes the following data:
timestamp
string
ISO 8601 timestamp of when the event occurred.Example: "2024-03-11T14:23:45.123Z"
action
string
Event type, always "page_hit" for page views.
session_id
string
Unique identifier for the user session (UUID v4).Example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
version
string
Tracker version, currently "1".
tenant_id
string
Tenant identifier (if configured with data-tenant-id).
domain
string
Domain identifier (if configured with data-domain).
payload
object
Contains detailed page information:

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.

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

Build docs developers (and LLMs) love