Overview
Beyond automatic page view tracking, the Tinybird tracker provides a JavaScript API for tracking custom events. Use this to capture:
- User interactions - Clicks, form submissions, scrolls
- Business events - Add to cart, purchases, sign-ups
- Feature usage - Tool usage, settings changes
- Custom metrics - Any domain-specific events
Basic Usage
Once the tracker script is loaded, use the global Tinybird.trackEvent() function:
Tinybird.trackEvent('event_name', {
// Your custom properties
})
Function Signature
Event name identifier. Use descriptive, snake_case names.Examples: add_to_cart, purchase_complete, video_play
Event properties as a JavaScript object. Can include any custom data relevant to your event.Payload must be between 2 and 10,240 bytes when stringified.
Examples
E-Commerce Events
Tinybird.trackEvent('add_to_cart', {
partnumber: 'A1708 (EMC 3164)',
quantity: 1,
price: 299.99,
currency: 'USD'
})
User Interactions
document.querySelector('#cta-button').addEventListener('click', () => {
Tinybird.trackEvent('cta_click', {
button_id: 'cta-button',
button_text: 'Get Started',
page: window.location.pathname
})
})
Feature Usage
Tinybird.trackEvent('search', {
query: searchInput.value,
results_count: results.length,
filters_applied: activeFilters.length
})
Event Data Structure
Custom events are sent with the same structure as page views:
{
"timestamp": "2024-03-11T14:23:45.123Z",
"action": "add_to_cart",
"version": "1",
"session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"tenant_id": "my-tenant",
"domain": "example.com",
"payload": {
"partnumber": "A1708 (EMC 3164)",
"quantity": 1,
"price": 299.99,
"currency": "USD"
}
}
The event name you specified in trackEvent().
The custom properties you provided, plus any global attributes.
Implementation Details
Event Sending Function
From the source code (middleware/src/index.js:224-283):
async function _sendEvent(name, payload) {
_setSessionId()
if (!_isValidUserAgent(window.navigator.userAgent)) {
return
}
let url
if (proxyUrl) {
url = proxyUrl
} else if (proxy) {
url = `${proxy}/api/tracking`
} else if (host) {
host = host.replaceAll(/\/+$/gm, '')
url = `${host}/v0/events?name=${DATASOURCE}&token=${token}`
} else {
url = `https://api.tinybird.co/v0/events?name=${DATASOURCE}&token=${token}`
}
let processedPayload
if (stringifyPayload) {
processedPayload = _maskSuspiciousAttributes(payload)
processedPayload = Object.assign({}, JSON.parse(processedPayload), globalAttributes)
processedPayload = JSON.stringify(processedPayload)
if (!_isValidPayload(processedPayload)) {
return
}
} else {
processedPayload = Object.assign({}, payload, globalAttributes)
const maskedStr = _maskSuspiciousAttributes(processedPayload)
if (!_isValidPayload(maskedStr)) {
return
}
processedPayload = JSON.parse(maskedStr)
}
const session_id = _getSessionId() || _uuidv4()
const request = new XMLHttpRequest()
request.open('POST', url, true)
request.setRequestHeader('Content-Type', 'application/json')
request.send(
JSON.stringify({
timestamp: new Date().toISOString(),
action: name,
version: '1',
session_id,
tenant_id: tenantId,
domain,
payload: processedPayload,
})
)
}
// Exposed to window
window.Tinybird = { trackEvent: _sendEvent }
Global Attributes
You can define attributes that will be included in every event using the data-tb-* prefix on the script tag.
Usage
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
data-tb-app-version="2.1.0"
data-tb-environment="production"
data-tb-user-segment="premium"
></script>
Result
Every event will include these attributes in the payload:
{
"action": "add_to_cart",
"payload": {
"partnumber": "A1708 (EMC 3164)",
"quantity": 1,
"app_version": "2.1.0",
"environment": "production",
"user_segment": "premium"
}
}
Hyphens in attribute names are automatically converted to underscores.
data-tb-my-custom-attr becomes my_custom_attr
Implementation
From the source code (middleware/src/index.js:33-40):
let globalAttributes = {}
for (const attr of document.currentScript.attributes) {
if (attr.name.startsWith('tb_')) {
globalAttributes[attr.name.slice(3).replace(/-/g, '_')] = attr.value
}
if (attr.name.startsWith('data-tb-')) {
globalAttributes[attr.name.slice(8).replace(/-/g, '_')] = attr.value
}
}
Privacy & Security
Automatic PII Masking
The tracker automatically masks potentially sensitive fields:
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',
]
Before masking:
After masking:
{
"email": "********",
"order_id": "********"
}
While automatic masking helps, never intentionally track PII like passwords, credit card numbers, or personal identification numbers.
Payload Validation
Events are validated before sending:
function _isValidPayload(payloadStr) {
if (!payloadStr || typeof payloadStr !== 'string') {
return false
}
if (payloadStr.length < 2 || payloadStr.length > 10240) {
return false
}
return true
}
- Minimum: 2 bytes
- Maximum: 10,240 bytes (10 KB)
function _isValidUserAgent(userAgent) {
if (!userAgent || typeof userAgent !== 'string') {
return true
}
if (userAgent.length > 500) {
return false
}
return true
}
User agents over 500 characters are rejected.
Best Practices
Use snake_case for event names and property keys:Good:Tinybird.trackEvent('add_to_cart', {
product_id: '123',
product_name: 'Analytics Tool'
})
Bad:Tinybird.trackEvent('AddToCart', {
ProductID: '123',
'Product Name': 'Analytics Tool'
})
Only include necessary data. Large payloads may be rejected.Good:Tinybird.trackEvent('search', {
query: 'analytics',
results: 42
})
Bad:Tinybird.trackEvent('search', {
query: 'analytics',
results: results.map(r => ({ /* full object */ }))
})
Event names should describe what happened, not implementation details.Good:
purchase_complete
video_play
signup_start
Bad:
button_click
div_123_interaction
function_called
Add contextual information to help with analysis.Tinybird.trackEvent('error_occurred', {
error_code: 'AUTH_001',
error_message: 'Invalid token',
page: window.location.pathname,
user_action: 'login_attempt'
})
Testing Events
To test your custom events during development:
-
Open browser console and verify the Tinybird object exists:
console.log(window.Tinybird)
// { trackEvent: ƒ _sendEvent(name, payload) }
-
Manually trigger events:
Tinybird.trackEvent('test_event', { test: true })
-
Check network tab for POST requests to Tinybird
-
Verify in Tinybird UI that events appear in your data source
Framework Integration
import { useEffect } from 'react'
function ProductCard({ product }) {
const handleAddToCart = () => {
// Track event
window.Tinybird.trackEvent('add_to_cart', {
product_id: product.id,
product_name: product.name,
price: product.price
})
// Add to cart logic
addToCart(product)
}
return (
<button onClick={handleAddToCart}>
Add to Cart
</button>
)
}
<template>
<button @click="handleAddToCart">
Add to Cart
</button>
</template>
<script>
export default {
methods: {
handleAddToCart() {
// Track event
window.Tinybird.trackEvent('add_to_cart', {
product_id: this.product.id,
product_name: this.product.name,
price: this.product.price
})
// Add to cart logic
this.addToCart(this.product)
}
}
}
</script>
'use client'
import { useEffect } from 'react'
export default function ProductPage({ product }) {
useEffect(() => {
// Track page view with custom data
if (window.Tinybird) {
window.Tinybird.trackEvent('product_view', {
product_id: product.id,
product_name: product.name,
category: product.category
})
}
}, [product])
return <div>...</div>
}
Next Steps
Web Vitals
Track Core Web Vitals performance metrics
Configuration
Advanced configuration options