React Router V7 framework mode (Remix V3)
Contents
This guide walks you through setting up PostHog for React Router V7 in framework mode. If you're using React Router in another mode, find the guide for that mode in the React Router page. If you're using React with another framework, go to the React integration guide.
- 1
Install client-side SDKs
RequiredFirst, you'll need to install
posthog-jsand@posthog/reactusing your package manager. These packages allow you to capture client-side events.In framework mode, you'll also need to set
posthog-jsand@posthog/reactas external packages in yourvite.config.tsfile to avoid SSR errors.vite.config.ts - 2
Add your environment variables
RequiredAdd your environment variables to your
.env.localfile and to your hosting provider (e.g. Vercel, Netlify, AWS). You can find your project API key and host in your project settings. If you're using Vite, includingVITE_PUBLIC_in their names ensures they are accessible in the frontend..env.local - 3
Add the PostHogProvider to your app
RequiredIn framework mode, your app enters from the
app/entry.client.tsxfile. In this file, you'll need to initialize the PostHog SDK and pass it to your app through thePostHogProvidercontext.app/entry.client.tsxTo help PostHog track your user sessions across the client and server, you'll need to add the
__add_tracing_headers: ['your-backend-domain1.com', 'your-backend-domain2.com', ...]option to your PostHog initialization. This adds theX-POSTHOG-DISTINCT-IDandX-POSTHOG-SESSION-IDheaders to your requests, which we'll later use on the server-side. Verify client-side events are captured
CheckpointConfirm that you can capture client-side events and see them in your PostHog projectAt this point, you should be able to capture client-side events and see them in your PostHog project. This includes basic events like page views and button clicks that are autocaptured.
You can also try to capture a custom event to verify it's working. You can access PostHog in any component using the
usePostHoghook.TSXYou should see these events in a minute or two in the activity tab.
- 4
Access PostHog methods
RequiredOn the client-side, you can access the PostHog client using the
usePostHoghook. This hook returns the initialized PostHog client, which you can use to call PostHog methods. For example:TSXFor a complete list of available methods, see the posthog-js documentation.
- 5
Identify your user
RecommendedNow that you can capture basic client-side events, you'll want to identify your user so you can associate users with captured events.
Generally, you identify users when they log in or when they input some identifiable information (e.g. email, name, etc.). You can identify users by calling the
identifymethod on the PostHog client:TSXPostHog automatically generates anonymous IDs for users before they're identified. When you call identify, a new identified person is created. All previous events tracked with the anonymous ID link to the new identified distinct ID, and all future captures on the same browser associate with the identified person.
- 6
Install server-side SDKs
RecommendedInstall the PostHog Node SDK using your package manager. This is the SDK you'll use to capture server-side events.
- 7
Create a server-side middleware
RecommendedNext, create a server-side middleware to help you capture server-side events. This middleware helps you achieve the following:
- Initialize a PostHog client
- Fetch the session and distinct ID from the
X-POSTHOG-SESSION-IDandX-POSTHOG-DISTINCT-IDheaders and pass them to your request as a context. This automatically identifies the user and session for you in all subsequent event captures. - Calls
shutdown()on the PostHog client to ensure all events are sent before the request is completed.
app/lib/posthog-middleware.tsThen, you'll need to register the middleware in your app in the
app/root.tsxfile by exporting it in theRoute.MiddlewareFunction[]array.app/root.tsx Verify server-side events are captured
CheckpointConfirm that you can capture server-side events and see them in your PostHog projectAt this point, you should be able to capture server-side events and see them in your PostHog project.
In a route, you can access the PostHog client from the context and capture an event. The middleware assigns the session ID and the distinct ID. This ensures that the system associates events with the correct user and session.
app/routes/api.checkout.ts- 8
Create an error boundary
RecommendedPostHog can capture exceptions thrown in your app through an error boundary. React Router in framework mode has a built-in error boundary that you can use to capture exceptions. You can create an error boundary by exporting
ErrorBoundaryfrom yourapp/root.tsxfile.app/root.tsxThis automatically captures exceptions thrown in your React Router app using the
posthog.captureException()method. - 9
Next steps
RecommendedNow that you've set up PostHog for React Router, you can start capturing events and exceptions in your app.
To get the most out of PostHog, you should familiarize yourself with the following:
- PostHog Web SDK docs: Learn more about the PostHog Web SDK and how to use it on the client-side.
- PostHog Node SDK docs: Learn more about the PostHog Node SDK and how to use it on the server-side.
- Identify users: Learn more about how to identify users in your app.
- Group analytics: Learn more about how to use group analytics in your app.
- PostHog AI: After capturing events, use PostHog AI to help you understand your data and build insights.
TypeError: Cannot read properties of undefined
If you see the error TypeError: Cannot read properties of undefined (reading '...') this is likely because you tried to call a posthog function when posthog was not initialized (such as during the initial render). On purpose, we still render the children even if PostHog is not initialized so that your app still loads even if PostHog can't load.
To fix this error, add a check that posthog has been initialized such as:
Typescript helps protect against these errors.
Tracking element visibility
The PostHogCaptureOnViewed component enables you to automatically capture events when elements scroll into view in the browser. This is useful for tracking impressions of important content, monitoring user engagement with specific sections, or understanding which parts of your page users are actually seeing.
The component wraps your content and sends a $element_viewed event to PostHog when the wrapped element becomes visible in the viewport. It only fires once per component instance.
Basic usage:
With custom properties:
You can include additional properties with the event to provide more context:
Tracking multiple children:
Use trackAllChildren to track each child element separately. This is useful for galleries or lists where you want to know which specific items were viewed:
When trackAllChildren is enabled, each child element sends its own event with a child_index property indicating its position.
Custom intersection observer options:
You can customize when elements are considered "viewed" by passing options to the IntersectionObserver:
The component passes all other props to the wrapper div, so you can add styling, classes, or other HTML attributes as needed.
Feature flags
PostHog's feature flags enable you to safely deploy and roll back new features as well as target specific users and groups with them.
There are two ways to implement feature flags in React:
- Using hooks.
- Using the
<PostHogFeature>component.
Method 1: Using hooks
PostHog provides several hooks to make it easy to use feature flags in your React app.
| Hook | Description |
|---|---|
useFeatureFlagEnabled | Returns a boolean indicating whether the feature flag is enabled. This sends a $feature_flag_called event. |
useFeatureFlagVariantKey | Returns the variant key of the feature flag. This sends a $feature_flag_called event. |
useActiveFeatureFlags | Returns an array of active feature flags. This does not send a $feature_flag_called event. |
useFeatureFlagPayload | Returns the payload of the feature flag. This does not send a $feature_flag_called event. Always use this with useFeatureFlagEnabled or useFeatureFlagVariantKey. |
Example 1: Using a boolean feature flag
Example 2: Using a multivariate feature flag
Example 3: Using a flag payload
The useFeatureFlagPayload hook does not send a $feature_flag_called event, which is required for the experiment to be tracked. To ensure the exposure event is sent, you should always use the useFeatureFlagPayload hook with either the useFeatureFlagEnabled or useFeatureFlagVariantKey hook.
Method 2: Using the PostHogFeature component
The PostHogFeature component simplifies code by handling feature flag related logic.
It also automatically captures metrics, like how many times a user interacts with this feature.
Note: You still need the
PostHogProviderat the top level for this to work.
Here is an example:
The
matchon the component can be eithertrue, or the variant key, to match on a specific variant.If you also want to show a default message, you can pass these in the
fallbackattribute.
If you wish to customise logic around when the component is considered visible, you can pass in visibilityObserverOptions to the feature. These take the same options as the IntersectionObserver API. By default, we use a threshold of 0.1.
Payloads
If your flag has a payload, you can pass a function to children whose first argument is the payload. For example:
Request timeout
You can configure the feature_flag_request_timeout_ms parameter when initializing your PostHog client to set a flag request timeout. This helps prevent your code from being blocked in the case when PostHog's servers are too slow to respond. By default, this is set at 3 seconds.
Error handling
When using the PostHog SDK, it's important to handle potential errors that may occur during feature flag operations. Here's an example of how to wrap PostHog SDK methods in an error handler:
Bootstrapping flags
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.
For details on how to implement bootstrapping, see our bootstrapping guide.
Experiments (A/B tests)
Since experiments use feature flags, the code for running an experiment is very similar to the feature flags code:
It's also possible to run experiments without using feature flags.