tracklane
One interface between your application and every tool that receives events.
The same event, both sides
Two independent libraries that share only the vocabulary — which is what makes an event mean the same thing in the browser and in your backend.
import { createTracking, ga4 } from 'tracklane/browser';
const { track, identify, consent } = createTracking({
providers: [ga4('G-XXXXXXX')],
onError: (error) => report(error),
});
track('purchase', { transaction_id: 'T-1024', value: 49.9, currency: 'BRL' });
identify({ userId: 'user_42', email: 'ana@example.com' }, { plan: 'pro' });
consent('update', { ad_storage: 'granted', analytics_storage: 'granted' });import { createTracking, ga4 } from 'tracklane/server';
const { track } = createTracking({
providers: [ga4({ measurementId: 'G-XXXXXXX', apiSecret })],
});
await track('purchase', order, {
cookies: request.headers.get('cookie'),
user: { userId: order.userId, email: order.email },
dedupId: order.id,
timestamp: order.paidAt,
});One event, every tool
Name what happened once, in GA4 vocabulary. Each provider translates it into its own. Adding a tool is a line of configuration, not a diff across your application.
Browser and server
Two independent libraries sharing only the vocabulary, so an event means the same thing on both sides. The conversion APIs are half the problem, and each provider covers both.
No opinions
It never withholds a send on policy grounds, holds no consent state, and installs no vendor tags. What reaches a tool is what a hand-written call would have sent — including the errors.
Nothing you did not ask for
No queue, no retry, no delivery guarantee, no runtime dependencies. One vendor failing never affects another, and never reaches your call site.
Destinations
- GA4
- Meta — not yet
- LinkedIn — not yet
- PostHog — not yet
- X — not yet
Any tool that receives events about what your users do can be a destination — not just ad pixels. Writing your own uses the same contract the built-in ones use: no registry entry, no allow-list.