PostHog makes it easy to get data about traffic and usage of your React app. Integrating PostHog into your site enables analytics about user behavior, custom events capture, session recordings, feature flags, and more.
This guide will walk you through an example integration of PostHog using React and the posthog-js library.
Installation
For installation with Next.js we'd recommend following the Next.js integration guide instead.
- Install posthog-js using your package manager
yarn add posthog-js# ornpm install --save posthog-js
- Add your environment variables to your
.env.local
file and to your hosting provider (e.g. Vercel, Netlify, AWS). You can find your project API key in the PostHog app under Project Settings > API Keys.
REACT_APP_PUBLIC_POSTHOG_KEY=<ph_project_api_key>REACT_APP_PUBLIC_POSTHOG_HOST=<ph_instance_address>
- Integrate PostHog at the root of your app (
src/index.js
for the defaultcreate-react-app
).
// src/index.jsimport React from 'react';import ReactDOM from 'react-dom/client';import App from './App';import { PostHogProvider} from 'posthog-js/react'const options = {api_host: process.env.REACT_APP_PUBLIC_POSTHOG_HOST,}const root = ReactDOM.createRoot(document.getElementById('root'));root.render(<React.StrictMode><PostHogProviderapiKey={process.env.REACT_APP_PUBLIC_POSTHOG_KEY}options={options}><App /></PostHogProvider></React.StrictMode>);
Usage
PostHog Provider
The React context provider makes it easy to access the posthog-js
library in your app.
The provider can either take an initialized client instance OR an API key and an optional options
object.
With an initialized client instance:
// src/index.jsimport posthog from 'posthog-js';import { PostHogProvider} from 'posthog-js/react'posthog.init(process.env.REACT_APP_PUBLIC_POSTHOG_KEY,{api_host: process.env.REACT_APP_PUBLIC_POSTHOG_HOST,});const root = ReactDOM.createRoot(document.getElementById('root'));root.render(<React.StrictMode><PostHogProvider client={posthog}><App /></PostHogProvider></React.StrictMode>);
or with an API key and optional options
object:
// src/index.jsimport { PostHogProvider} from 'posthog-js/react'const options = {api_host: process.env.REACT_APP_PUBLIC_POSTHOG_HOST,}const root = ReactDOM.createRoot(document.getElementById('root'));root.render(<React.StrictMode><PostHogProviderapiKey={process.env.REACT_APP_PUBLIC_POSTHOG_KEY}options={options}><App /></PostHogProvider></React.StrictMode>);
Using posthog-js functions
By default, the posthog-js
library automatically captures pageviews, element clicks, inputs, and more. Autocapture can be tuned in with the configuration options.
If you want to use the library to identify users, capture events, use feature flags, or use other features, you can access the initialized posthog-js
library using the usePostHog
hook.
Do not directly import posthog
apart from installation as shown above. This will likely cause errors as the library might not be initialized yet. Initialization is handled automatically when you use the PostHogProvider
and hook.
All the methods of the library are available and can be used as described in the posthog-js documentation.
import { usePostHog } from 'posthog-js/react'import { useEffect } from 'react'import { useUser, useLogin } from '../lib/user'function App() {const posthog = usePostHog()const login = useLogin()const user = useUser()useEffect(() => {if (user) {posthog?.identify(user, {email: user.email,})posthog?.group('company', user.company_id)}}, [posthog, user])const loginClicked = () => {posthog?.capture('Clicked Log In')login()}return (<div className="App">{/* Fire a custom event when the button is clicked */}<button onClick={() => posthog?.capture('button clicked')}>Click me</button>{/* This button click event is autocaptured by default */}<button data-attr="autocapture-button">Autocapture buttons</button>{/* This button click event is not autocaptured */}<button className="ph-no-capture">Ignore certain elements</button><button onClick={loginClicked}>Login</button></div>)}export default App
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:
useEffect(() => {posthog?.capture('Test') // using optional chaining (recommended)if (posthog) {posthog.capture('Test') // using an if statement}}, [posthog])
Typescript helps protect against these errors.
Feature flags
Feature flags are a powerful way to test new features and roll them out to a subset of your users. You can use feature flags to enable/disable features, change the behavior of a feature, or even change the UI of a feature.
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. |
useFeatureFlagPayload | Returns the payload of the feature flag. |
useFeatureFlagVariantKey | Returns the variant key of the feature flag. |
useActiveFeatureFlags | Returns an array of active feature flags. |
For example, to show a welcome message if the feature flag is enabled:
import { useFeatureFlagEnabled } from 'posthog-js/react'function App() {const showWelcomeMessage = useFeatureFlagEnabled('cool-flag')return (<div className="App">{showWelcomeMessage ? (<div><h1>Welcome!</h1><p>Thanks for trying out our feature flags.</p></div>) : (<div><h2>No welcome message</h2><p>Because the feature flag evaluated to false.</p></div>)}</div>);}export default App;
To show a different message depending on the variant key:
import { useFeatureFlagVariantKey } from 'posthog-js/react'import { useState, useEffect } from 'react'function App() {const variantKey = useFeatureFlagVariantKey('show-welcome-message')const [ welcomeMessage, setWelcomeMessage ] = useState('')useEffect(() => {if (variantKey === 'variant-a') {setWelcomeMessage('Welcome to the Alpha!')} else if (variantKey === 'variant-b') {setWelcomeMessage('Welcome to the Beta!')}}, [variantKey])return (<div className="App">{welcomeMessage ? (<div><h1>{welcomeMessage}</h1><p>Thanks for trying out our feature flags.</p></div>) : (<div><h2>No welcome message</h2><p>Because the feature flag evaluated to false.</p></div>)}</div>);}export default App;
To load the message and title from the feature flag payload:
import { useFeatureFlagPayload } from 'posthog-js/react'function App() {const payload = useFeatureFlagPayload('show-welcome-message')return (<>{payload?.welcomeMessage ? (<div className="welcome-message"><h2>{payload?.welcomeTitle}</h2><p>{payload.welcomeMessage}</p></div>) : <div><h2>No welcome message</h2><p>Because the feature flag evaluated to false.</p></div>}</>)}
Feature flags React Component
If you don't wish to handle any kind of feature flag related logic handling, you can use our react component instead of the hooks above.
This has the added benefit of capturing metrics like how many times a user interacts with this feature automatically.
You still need the PostHogProvider
at the top level for this to work.
import { PostHogFeature } from 'posthog-js/react'function App() {return (<PostHogFeature flag='show-welcome-message' match={true}><div><h1>Hello</h1><p>Thanks for trying out our feature flags.</p></div></PostHogFeature>)}
The match
on the component can be either true
, 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 fallback
attribute.
If your flag has a payload, you can pass a function to children whose first argument is the payload. For example:
import { PostHogFeature } from 'posthog-js/react'function App() {return (<PostHogFeature flag='show-welcome-message' match={true}>{(payload) => {return (<div><h1>{payload.welcomeMessage}</h1><p>Thanks for trying out our feature flags.</p></div>)}}</PostHogFeature>)}
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.
Customising analytics
By default, this component tracks views and interactions. However, you can disable these by setting trackInteraction
and trackView
on the component like so:
<PostHogFeature flag={'show-hello'} match={true} trackInteraction={false} trackView={false}><div data-testid="helloDiv">Hello</div></PostHogFeature>