PostHog
Use PostHog for MainSDK product analytics and feature flags - US or EU cloud, project API key, main.yaml, consent, local testing and swapping.
PostHog is a product analytics platform with feature flags built in, and it sits behind two MainSDK interfaces. Through analytics your app tracks events, page views and identified users into PostHog, with nothing sent before consent where consent is required. Through flags your code asks PostHog whether a feature is on for a user, while the flags and targeting rules stay in PostHog. Pick PostHog when you want analytics and flags in one tool, or when you want the option to self-host, since PostHog is open source.
Interfaces it serves
Pick PostHog in main.yaml for any of these. Your calls stay the same, and you can swap later.
analyticsBuilding for 0.3Driver posthog PostHog
analytics:
driver: posthogSwap to another provider
flagsPreviewDriver posthog PostHog
planned; through the openfeature driver
flags:
driver: posthogSwap to another provider
Before you start
You need a PostHog account, on PostHog Cloud or a self-hosted instance.
- Pick a region. PostHog Cloud runs separate US and EU instances, and you choose one when you sign up. The region decides where your events are stored and which host the driver talks to; pick the one that matches your data rules.
- Create a project. PostHog keeps events, people and flags per project. Use one project for production and a separate one for development and stage, so test events never mix with real ones.
- Copy the project API key from the project settings. It identifies the project when sending events and evaluating flags.
- Create your flags in PostHog's feature flags area, with the rollout and targeting rules you want.
Store the keys in your secrets manager, never in code or a committed env file.
Configure
Each interface names its driver. The key names for PostHog are not defined yet.
analytics:
driver: posthog
# [to be decided] project API key (suggested secret:POSTHOG_PROJECT_KEY) and region (us | eu)flags:
driver: posthogPoint each environment's secrets at the matching PostHog project. Consent signals come from privacy, including a consent platform through IAB TCF or GPP, so you do not configure consent in PostHog as well.
Use it
Track an event on the server after a user starts checkout:
"use server";
import { analytics, auth } from "mainsdk";
export async function startCheckout(req: Request, plan: string) {
const session = await auth.require(req);
analytics.track("checkout.started", { plan }, { for: session.user });
}analytics.identify(user) after sign-in and analytics.page() in the browser work the same way. The user reaches PostHog only as a pseudonymised ID.
Ask PostHog whether a flag is on:
import { auth, flags } from "mainsdk";
export async function GET(req: Request) {
const session = await auth.require(req);
const useNew = await flags.enabled("new-checkout", { for: session.user });
const rowLimit = await flags.value("export.row-limit", { for: session.user });
return Response.json({ checkout: useNew ? "new" : "classic", rowLimit });
}Local development and testing
- A development project: point development and stage at a separate PostHog project so you can watch real events arrive without touching production data.
- Tests:
createTestApp()from@mainsdk/testingrunsanalyticsandflagson in-memory fakes, so tests send nothing to PostHog. Helpers to set consent and flag values on the fakes arrive with the specs. - Outages:
chaos.on("analytics", { outage: true })andchaos.on("flags", { outage: true })simulate PostHog being down, so you can check what your app does when a flag cannot be read.
import { it } from "node:test";
import { createTestApp, chaos } from "@mainsdk/testing";
it("still serves checkout when PostHog is down", async () => {
createTestApp({ main: "./main.yaml" });
chaos.on("flags", { outage: true });
// ... call GET /api/checkout and assert it still answers
});What MainSDK adds on top
- Nothing is sent to PostHog without consent where it is required, read from
privacyor your consent platform. - Only pseudonymised user IDs leave your app, so erasing a user in PostHog is a mapping delete.
- Flags and targeting stay in PostHog; MainSDK does not copy them out.
- The same
track,identify,page,enabledandvaluecalls work with every listed driver.
See Product analytics and Feature flags for the full contracts.
Limits and notes
- Both drivers are planned from the product requirements; neither interface has a spec yet.
- PostHog Cloud offers US and EU regions. MainSDK regions are US, EU and India; for India, choose the PostHog region your data rules allow, or self-host.
- Keep personal data out of event properties and flag names. Pseudonymisation covers the user ID, not what you put in properties.
- A flag is not a permission. Decide who may do what with Permissions.
- Whether
flagscan evaluate a flag without a user is not decided yet.
Swap to or from it
Change the driver for either interface on its own:
analytics:
driver: plausible # was posthogYour calls stay the same. Past events stay in PostHog; new events go to the new tool.
FAQ
Can I use PostHog for flags and a different tool for analytics? Yes. Each interface names its own driver, so analytics: { driver: plausible } and flags: { driver: posthog } work together.
Do I need PostHog's own snippet as well? No. Track through analytics, so consent and pseudonymisation apply to every event. A second snippet would bypass both.
Can I self-host PostHog? PostHog is open source and can be self-hosted. How the driver is pointed at a self-hosted instance is not defined yet.
Next
Open questions
Was this page helpful?