Client-side bootstrapping
Contents
Note: Bootstrapping feature flags is available in our JavaScript web, React Native, iOS, Android, and Flutter SDKs. Mobile SDKs persist state on-device, so bootstrapped identity applies on the first launch only. See behavior on mobile SDKs.
Since there is a delay between initializing PostHog and fetching feature flags, feature flags are not always available immediately. This makes them unusable if you want to do something like redirecting a user to a different page based on a feature flag.
To have your feature flags available immediately, you can initialize PostHog with precomputed values until it has had a chance to fetch them. This is called bootstrapping. After the SDK fetches feature flags from PostHog, it will use those flag values instead of bootstrapped ones.
To bootstrap PostHog with feature flags, use the bootstrap key in the initialization config and add feature flag values to it:
Setting the correct flag values
To ensure you are bootstrapping PostHog with the correct flag values, we recommend fetching the flags values from your server alongside the page load request, and then passing them to your frontend.
You can do this by:
- When receiving a frontend request, fetch all the feature flags for the user on the backend by calling
getAllFlags(). - Return the frontend request with the flags values in the response.
- On the frontend, get the flag values from the response and add them to the
bootstrapkey in the PostHog initialization.
Tip: to ensure your request is as quick as possible, use local evaluation on your server.
Bootstrapping in single-page apps
In environments where posthog.init only runs once during a session (for example, SPAs like Next.js using instrumentation-client), you have two options:
- Server-side pre-evaluation: Evaluate flags before the app renders, then pass the values into your app and add them to
bootstrapor use them directly. - Client-side pre-evaluation: Evaluate the flag in an earlier page/state than the one where you need the value, then store and reuse it immediately when required.
Both approaches avoid flicker and achieve the same outcome as bootstrapping, as long as you use the same distinct_id across client and server.
Bootstrapping with a distinct ID
The bootstrap object has four optional arguments: distinctID, isIdentifiedID, sessionID, and featureFlags.
distinctIDenables you to set distinct ID of the user during PostHog's initialization. This is useful when you want to ensure the distinct ID on your frontend is the same as the distinct ID that you calledgetAllFlags()with on your server. It is strongly recommended to include it.
Note: The only case where you don't want to include
distinctIDis when your user has an existing PostHog session and you have not yet calledidentify()at any point in time to link the anonymous session ID with their identified ID. In this case, bootstrappingdistinctIDwill cause PostHog to drop the anonymous session ID and the two sessions will not be linked.
isIdentifiedIDensures that thedistinctIDis treated as an identified ID in the library. This is helpful as it warns you when you try to do something wrong with this ID, like calling identify again with a different ID. Set this totrueif you're using a unique ID such asemailor a database ID. Set this tofalseif you're generating a random anonymous ID.sessionIDenables you to connect sessions across domains. This enables you get an accurate session count and complete session replays. To get the session ID, callposthog.get_session_id().featureFlagsenables flag values to be available as soon as PostHog loads. Callposthog.getAllFlags()(or equivalent method) in your backend and pass the values to your frontend in thebootstrapobject.
Behavior on mobile SDKs
The iOS, Android, and Flutter SDKs bootstrap through PostHogBootstrapConfig and persist identity and flags on the device, so bootstrapping behaves slightly differently than on the web. Assign config.bootstrap = PostHogBootstrapConfig(...) before calling setup(). Bootstrapping requires iOS SDK 3.66.0+, Android SDK 3.55.0+, or Flutter SDK 5.31.0+.
React Native is also a mobile SDK, but it uses the posthog-js-style
bootstrapoption shown in the examples above (notPostHogBootstrapConfig) and follows the standard behavior.
- Bootstrapped identity applies to the first session. Setting it before
setup()means events captured synchronously during initialization (likeApplication Installed) carry your distinct ID instead of the SDK-generated UUID.- An anonymous bootstrap (
isIdentifiedId: false, the default) seeds the anonymous ID only when none is persisted yet. Once an anonymous ID exists on disk, or the user has been identified, it is ignored. - An identified bootstrap (
isIdentifiedId: true) is for a user you've already identified outside the SDK (for example, from a backend session token). On a fresh install it seeds the distinct ID and marks the user identified. On a returning install where an anonymous user already exists, it merges that user into the identified ID viaidentify(), which emits a$identifyevent duringsetup(). A different, already-identified user is left untouched. The identified ID never becomes the device ID.
- An anonymous bootstrap (
- Bootstrapped flags are served until the first
/flagsresponse, then replaced. A complete/flagsresponse takes over entirely, so bootstrapped-only keys don't persist past it. Only enabled flags are seeded: atrueboolean or a non-empty variant string. Afalseor empty value is dropped, matching posthog-js. Seed payloads with the separatefeatureFlagPayloadsoption. Flag values and payloads must be JSON-serializable, or they're dropped. Bootstrapped flags are cleared onreset().
The feature-flags-loaded signal fires as soon as bootstrapped flags are applied, so startup logic can read them immediately. These SDKs don't support the sessionID bootstrap option.
Flutter web:
bootstrapis not applied on Flutter web because the web SDK hooks onto an already-initializedposthog-jsinstance. Configurebootstrapin yourposthog.init({...})call instead.
Overriding feature flags
Bootstrapped feature flag values are temporary and are disregarded after PostHog fetches flag values. If you are trying to override feature flag values in a persistent manner, some PostHog SDKs support overriding flags: