Overview
Custom attributes allow you to attach additional metadata to every event tracked by the analytics script. These attributes are automatically included in the payload field of your analytics_events datasource and can be used for filtering, segmentation, and custom analysis.
Attribute Naming Convention
All custom attributes must use the data-tb- prefix to be recognized and stored.
The prefix ensures that custom attributes are:
- Distinguished from standard HTML data attributes
- Automatically captured by the tracking script
- Included in every event payload
Adding Custom Attributes
Basic Usage
Add custom attributes directly to your tracking script tag:
<script
defer
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
data-tb-environment="production"
data-tb-app-version="2.5.0"
data-tb-customer-tier="premium"
></script>
How Attributes are Processed
The tracking script converts attribute names and stores them in the payload:
- Strip prefix:
data-tb- is removed
- Convert hyphens: Hyphens are converted to underscores
- Store in payload: Attributes are added to the JSON payload
Example transformation:
data-tb-app-version → stored as app_version in payload
data-tb-customer-tier → stored as customer_tier in payload
Resulting Payload
With the above script, every event will include:
{
"environment": "production",
"app_version": "2.5.0",
"customer_tier": "premium",
"user-agent": "Mozilla/5.0...",
"locale": "en-US",
"location": "US",
"referrer": "https://example.com",
"pathname": "/dashboard",
"href": "https://app.example.com/dashboard"
}
Implementation Details
The tracking script processes custom attributes using this code:
let globalAttributes = {}
if (document.currentScript) {
for (const attr of document.currentScript.attributes) {
if (attr.name.startsWith('data-tb-')) {
globalAttributes[attr.name.slice(8).replace(/-/g, '_')] = attr.value
}
}
}
Attributes are then merged into every event:
processedPayload = Object.assign({}, payload, globalAttributes)
Common Use Cases
Application Environment
Track which environment events are coming from:
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
data-tb-environment="production"
data-tb-region="us-east-1"
></script>
User Segmentation
Include user plan or tier information:
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
data-tb-plan="enterprise"
data-tb-trial="false"
data-tb-onboarding-complete="true"
></script>
Application Version
Track app version for correlating analytics with releases:
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
data-tb-app-version="3.1.2"
data-tb-build="20240115-a3f2e1c"
></script>
Feature Flags
Monitor usage of experimental features:
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
data-tb-feature-new-editor="enabled"
data-tb-feature-ai-assist="enabled"
></script>
A/B Testing
Track experiment variations:
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
data-tb-experiment-checkout="variant-b"
data-tb-experiment-pricing="control"
></script>
Dynamic Attributes
For single-page applications or when attributes need to be set dynamically:
Setting Attributes at Runtime
// Create script element
const script = document.createElement('script');
script.src = 'https://unpkg.com/@tinybirdco/flock.js';
script.setAttribute('data-token', 'YOUR_TRACKER_TOKEN');
// Add custom attributes dynamically
script.setAttribute('data-tb-user-role', currentUser.role);
script.setAttribute('data-tb-workspace-id', currentWorkspace.id);
script.setAttribute('data-tb-feature-flags', JSON.stringify(featureFlags));
// Append to document
document.head.appendChild(script);
Server-Side Rendering
For frameworks like Next.js, Nuxt, or SvelteKit:
// React/Next.js example
import Script from 'next/script';
export default function AnalyticsScript({ user, workspace }) {
return (
<Script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token={process.env.NEXT_PUBLIC_TINYBIRD_TOKEN}
data-tb-user-id={user.id}
data-tb-user-role={user.role}
data-tb-workspace-id={workspace.id}
data-tb-subscription-tier={workspace.tier}
/>
);
}
Querying Custom Attributes
Custom attributes are stored in the payload column as JSON. Use ClickHouse JSON functions to query them:
SELECT
JSONExtractString(payload, 'customer_tier') as tier,
JSONExtractString(payload, 'app_version') as version,
count() as events
FROM analytics_events
WHERE toDate(timestamp) = today()
GROUP BY tier, version
ORDER BY events DESC
Creating Custom Pipes
Create a custom endpoint to analyze by custom attributes:
export const eventsByTier = defineEndpoint("events_by_tier", {
nodes: [
node({
sql: `
SELECT
JSONExtractString(payload, 'customer_tier') as tier,
toDate(timestamp) as date,
uniq(session_id) as visits,
count() as events
FROM analytics_events
WHERE timestamp >= {{ DateTime(date_from) }}
AND timestamp < {{ DateTime(date_to) }}
GROUP BY tier, date
ORDER BY date DESC, events DESC
`,
}),
],
params: {
date_from: p.dateTime().describe("Start date"),
date_to: p.dateTime().describe("End date"),
},
output: {
tier: t.string(),
date: t.date(),
visits: t.uint64(),
events: t.uint64(),
},
});
Filtering by Custom Attributes
SELECT
pathname,
count() as views
FROM analytics_events
WHERE action = 'page_hit'
AND JSONExtractString(payload, 'environment') = 'production'
AND JSONExtractString(payload, 'customer_tier') = 'enterprise'
GROUP BY pathname
ORDER BY views DESC
LIMIT 10
Best Practices
Do: Use descriptive, kebab-case names for attributes (e.g., data-tb-user-role)
Do: Keep attribute values simple (strings, numbers, booleans)
Do: Document your custom attributes and their possible values
Do: Use consistent naming conventions across your application
Don’t: Include PII (personally identifiable information) in custom attributes
Don’t: Store sensitive data like passwords, tokens, or API keys
Don’t: Use excessively long strings (keep under 100 characters per attribute)
Attribute Validation
The tracking script includes automatic validation:
- Payload size limit: Maximum 10KB (10,240 bytes)
- Payload structure: Must be valid JSON
- Automatic masking: Suspicious fields are automatically masked
Masked Attributes
For security, these attribute names are automatically masked:
username, user, user_id, userid
password, pass, pin, passcode
token, api_token
email, address, phone
sex, gender
order, order_id, orderid
payment, credit_card
Masked values are replaced with "********" before sending to Tinybird.
Advanced: Alternative Prefix
The tracking script also supports the legacy tb_ prefix (without data-):
<script
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="YOUR_TRACKER_TOKEN"
tb_environment="production"
></script>
The data-tb- prefix is recommended for better HTML5 compliance and consistency.
Example: Complete Implementation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My SaaS App</title>
<script
defer
src="https://unpkg.com/@tinybirdco/flock.js"
data-token="p.eyJ1IjogIjdhZGRiOGMy..."
data-tb-environment="production"
data-tb-app-version="2.5.0"
data-tb-build="20240315-a3f2e1c"
data-tb-region="us-east-1"
data-tb-plan="enterprise"
data-tb-feature-new-editor="enabled"
data-tb-experiment-checkout="variant-b"
></script>
</head>
<body>
<div id="app"></div>
<script>
// Track custom events with the same attributes
Tinybird.trackEvent('button_click', {
button: 'upgrade-plan',
location: 'header'
});
// All events will include the custom attributes
// from the script tag automatically
</script>
</body>
</html>
Every event (page hits and custom events) will include:
- Standard tracking data (user-agent, location, referrer, etc.)
- Your custom attributes (environment, app_version, build, etc.)
- Event-specific data (button, location, etc.)