Providers
Choose official providers, understand support by environment, and route events intentionally.
Providers translate trakoo events into the APIs of the services you already use. You can start with one provider, add another later, or route specific methods and event names to the tools that should receive them.
Client and server product analytics, feature flags, and session replay
OpenPanelClient and server web and product analytics with optional session replay
BentoClient and server lifecycle/email automation for identified users
PirschClient and server privacy-friendly web analytics
EmitKitServer-only event notifications with channel routing
VisitorsClient-only analytics with optional Stripe revenue attribution
ProxyBrowser queue + server ingest helpers for first-party event delivery
Support matrix
| Provider | Client import | Server import | Package | Best for |
|---|---|---|---|---|
| PostHog | PostHogClientProvider |
PostHogServerProvider |
@trakoo/posthog |
Product analytics across browser and backend |
| OpenPanel | OpenPanelClientProvider |
OpenPanelServerProvider |
@trakoo/openpanel |
Open-source web and product analytics |
| Bento | BentoClientProvider |
BentoServerProvider |
Client: trakoo; server: @trakoo/bento |
Identified lifecycle events and email automation |
| Pirsch | PirschClientProvider |
PirschServerProvider |
trakoo |
Privacy-friendly page views and lightweight events |
| EmitKit | — | EmitKitServerProvider |
@trakoo/emitkit |
Notifications and activity channels |
| Visitors | VisitorsClientProvider |
— | trakoo |
Lightweight web analytics and revenue attribution |
| Proxy | ProxyProvider |
ingestProxyEvents, createProxyHandler |
trakoo |
Sending browser events through your own API |
Providers included in trakoo import from trakoo/providers/client and trakoo/providers/server. Providers that wrap a vendor SDK ship as their own package; import them from its /client or /server subpath, such as @trakoo/posthog/client, and install only the SDK for that side. See Installation.
One event, multiple destinations
import { createClientAnalytics } from 'trakoo/client';
import {
BentoClientProvider,
VisitorsClientProvider
} from 'trakoo/providers/client';
import { PostHogClientProvider } from '@trakoo/posthog/client';
import { appEvents } from '@/lib/events';
const analytics = createClientAnalytics({
events: appEvents,
providers: [
new PostHogClientProvider({
token: import.meta.env.VITE_POSTHOG_KEY
}),
{
provider: new BentoClientProvider({
siteUuid: import.meta.env.VITE_BENTO_SITE_UUID
}),
methods: ['identify', 'track']
},
new VisitorsClientProvider({
token: import.meta.env.VITE_VISITORS_TOKEN,
persist: true
})
]
});
await analytics.track('user_signed_up', {
email: 'ada@example.com',
plan: 'pro'
});
The application emits one typed event. trakoo fans it out to each enabled provider.
Routing
Some providers should not receive every call. Bento is usually better for identified user lifecycle events than anonymous page views. EmitKit is usually better for selected events than high-volume click streams. Wrap a provider in a config object to control exactly which methods and events reach it.
import { createClientAnalytics } from 'trakoo/client';
import { BentoClientProvider } from 'trakoo/providers/client';
import { PostHogClientProvider } from '@trakoo/posthog/client';
import { appEvents } from '@/lib/events';
const analytics = createClientAnalytics({
events: appEvents,
providers: [
new PostHogClientProvider({ token: import.meta.env.VITE_POSTHOG_KEY }),
{
provider: new BentoClientProvider({
siteUuid: import.meta.env.VITE_BENTO_SITE_UUID
}),
methods: ['identify', 'track'],
eventPatterns: ['user_*', 'checkout_*']
}
]
});
Routing options are part of the shared provider config:
| Option | Effect |
|---|---|
methods |
Only call these provider methods (initialize, identify, track, pageView, pageLeave, reset, and on the server group and revenue) |
exclude |
Call every method except these |
events |
Only track these exact event names |
excludeEvents |
Track every event except these names |
eventPatterns |
Track events matching glob-style patterns such as newsletter_* |
pii |
false keeps personal data from this provider (default true) |
methods wins over exclude when both are set. events, eventPatterns, and excludeEvents are mutually exclusive; the runtime logs a warning and uses the highest-priority option.
Personal data
An email tool needs the user’s email; a product-analytics tool usually shouldn’t store it. Set pii: false on a provider entry to keep personal data from that provider while the others still receive it:
providers: [
{ provider: new OpenPanelServerProvider({ /* ... */ }), pii: false },
new BentoServerProvider({ /* ... */ }),
]
A provider with pii: false never receives the email, name, firstName, lastName, or phone traits in identify(), nor the email or those traits in an event’s user context. It still receives the user id, event properties, and the rest of the context. Keep personal data out of event properties themselves: pii doesn’t inspect them.
Browser-to-server delivery
Use the proxy when browser events should go through your domain before reaching server providers.
import { createClientAnalytics } from 'trakoo/client';
import { ProxyProvider } from 'trakoo/providers/client';
import { appEvents } from '@/lib/events';
export const analytics = createClientAnalytics({
events: appEvents,
providers: [
new ProxyProvider({
endpoint: '/api/events',
batch: { size: 10, interval: 2000 }
})
]
});
import { createServerAnalytics } from 'trakoo/server';
import { createProxyHandler } from 'trakoo/providers/server';
import { EmitKitServerProvider } from '@trakoo/emitkit/server';
import { appEvents } from '@/lib/events';
const serverAnalytics = createServerAnalytics({
events: appEvents,
providers: [
new EmitKitServerProvider({
apiKey: process.env.EMITKIT_API_KEY!,
channelName: 'product-events'
})
]
});
export const POST = createProxyHandler(serverAnalytics);
Build your own provider
If a service is not built in, implement the AnalyticsProvider interface or extend BaseAnalyticsProvider.
import {
BaseAnalyticsProvider,
type BaseEvent,
type EventContext
} from 'trakoo/client';
export class CustomProvider extends BaseAnalyticsProvider {
name = 'CustomProvider';
initialize() {}
track(event: BaseEvent, context?: EventContext) {
console.log(event.action, event.properties, context);
}
identify(userId: string, traits?: Record<string, unknown>) {}
pageView(properties?: Record<string, unknown>, context?: EventContext) {}
reset() {}
}
