> AI agents: this is one page from PostHog's docs. Full index of Markdown docs for LLMs: https://posthog.com/llms.txt # React - Docs Copy page # React - Docs 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 walks you through an example integration of PostHog using vanilla React and the [posthog-js library](/docs/integrate/client/js.md). ## Using a framework? Using React with a framework like Next.js, Remix, or React Router requires additional setup. Follow their respective guides instead: - [![](https://res.cloudinary.com/dmukukwp6/image/upload/posthog.com/contents/images/docs/integrate/frameworks/nextjs.svg)Next.js](/docs/libraries/next-js.md) - [![](https://res.cloudinary.com/dmukukwp6/image/upload/remix_letter_glowing_49183adce2.svg)Remix](/docs/libraries/remix.md) - [![](https://res.cloudinary.com/dmukukwp6/image/upload/rr_logo_light_970950178e.svg)React Router](/docs/libraries/react-router.md) ## Beta: integration via LLM Install PostHog for React in seconds with our wizard by running this prompt with [LLM coding agents](/blog/envoy-wizard-llm-agent.md) like Cursor and Bolt, or by running it in your terminal. `npx @posthog/wizard` [Learn more](/wizard.md) Or, to integrate manually, continue with the rest of this guide. ## Installation > For React-based frameworks, we recommend the [Next.js integration guide](/docs/libraries/next-js.md) and [Remix integration guide](/docs/libraries/remix.md) instead. 1. Install [`posthog-js`](https://github.com/posthog/posthog-js) and `@posthog/react` using your package manager: PostHog AI ### npm ```bash npm install --save posthog-js @posthog/react ``` ### Yarn ```bash yarn add posthog-js @posthog/react ``` ### pnpm ```bash pnpm add posthog-js @posthog/react ``` ### Bun ```bash bun add posthog-js @posthog/react ``` > **If your site sets a Content-Security-Policy**, it needs to allow PostHog. This applies to the snippet and to package installs alike: the SDK lazy-loads extra bundles (session replay, surveys) from PostHog's CDN, and sends events to the ingestion host. PostHog serves from subdomains of `posthog.com` that change over time, so allow the wildcard: > > PostHog AI > > ``` > script-src 'self' https://*.posthog.com; > connect-src 'self' https://*.posthog.com; > worker-src 'self' blob: data:; > ``` > > `script-src` covers the snippet and the lazy-loaded bundles, `connect-src` covers event ingestion and feature flags, and `worker-src` covers session replay. The [toolbar needs a few more](/docs/advanced/content-security-policy.md), or use a [reverse proxy](/docs/advanced/proxy.md) so everything is first-party. Failing to do so causes silent failures where `capture` and `identify` calls never send, so the integration looks complete while zero events arrive. Remember `connect-src` falls back to `default-src`, so `default-src 'self'` blocks event delivery even when the script itself is bundled. 2. Add your environment variables to your `.env.local` file and to your hosting provider (e.g. Vercel, Netlify, AWS). You can find your project token and host in [your project settings](https://us.posthog.com/settings/project). If you're using Vite, prefixing variable names with `VITE_` ensures they are accessible in the frontend. .env.local PostHog AI ```shell VITE_POSTHOG_PROJECT_TOKEN= VITE_POSTHOG_HOST=https://us.i.posthog.com ``` 3. Integrate PostHog at the root of your app (such as `main.jsx` for Vite apps and `root.tsx` for React Router V7). React PostHog AI ```jsx // src/main.jsx import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' import App from './App.jsx' import posthog from 'posthog-js'; import { PostHogProvider } from '@posthog/react' posthog.init(import.meta.env.VITE_POSTHOG_PROJECT_TOKEN, { api_host: import.meta.env.VITE_POSTHOG_HOST, defaults: '2026-05-30', }); createRoot(document.getElementById('root')).render( , ) ``` **Don't directly import PostHog** 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 `usePostHog` hook. ## Identifying users > **Identifying users is required.** Call `posthog.identify('your-user-id')` after login to link events to a known user. This is what connects frontend event captures, [session replays](/docs/session-replay.md), [LLM traces](/docs/ai-engineering.md), and [error tracking](/docs/error-tracking.md) to the same person — and lets backend events link back too. > > Use a stable ID from your auth system when possible, not an email or display name. Send those as person properties instead. If your app has no other stable key, email works as a fallback if they are unique. Never a shared literal like `"anonymous"` or `"user"`, which pools many people onto one person and corrupts their data. When no ID is available at all, skip the identify and retain the anonymous distinct ID that's automatically assigned. > > Call `posthog.reset()` on logout, so the next person to use the browser doesn't inherit the last one's identity. > > See our guide on [identifying users](/docs/getting-started/identify-users.md) for how to set this up. ## Usage ### PostHog provider The React context provider makes it easy to access the `posthog-js` library in your app. The provider takes an initialized and configured client instance like this: React PostHog AI ```jsx // src/index.js import posthog from 'posthog-js'; import { PostHogProvider} from '@posthog/react' posthog.init(process.env.REACT_APP_PUBLIC_POSTHOG_PROJECT_TOKEN, { api_host: process.env.REACT_APP_PUBLIC_POSTHOG_HOST, defaults: '2026-05-30', // Optional: send PostHog session/user context to your backend tracing_headers: ['api.example.com'], }); const root = ReactDOM.createRoot(document.getElementById('root')); root.render( ); ``` ### Calling PostHog methods By default, the `posthog-js` library automatically captures pageviews, element clicks, inputs, and more. Autocapture can be tuned in with [the configuration options](/docs/product-analytics/autocapture.md#configuring-autocapture). To call PostHog methods for actions like identifying users, capturing events, using feature flags, or using other features, you can access the initialized `posthog-js` library using the `usePostHog` hook. **Don't directly import PostHog** Always use the `usePostHog` hook to access the PostHog library. Directly importing `posthog` 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](/docs/libraries/js.md). If your React app calls your own backend, `tracing_headers` adds `X-POSTHOG-DISTINCT-ID` and `X-POSTHOG-SESSION-ID` to matching `fetch` and `XMLHttpRequest` requests. This lets server-side SDKs link backend events, errors, and LLM traces back to frontend sessions and replays. Use hostnames only, without protocols or paths. React PostHog AI ```jsx import { usePostHog } from '@posthog/react' import { useEffect } from 'react' import { useUser, useLogin } from '../lib/user' function App() { // `usePostHog`, like other React contexts, must be called at the top level of your component const posthog = usePostHog() const login = useLogin() const user = useUser() useEffect(() => { if (user) { // Identify sends an event, so you may want to limit how often you call it posthog?.identify(user.id, { email: user.email, }) posthog?.group('company', user.company_id) } }, [posthog, user.id, user.email, user.company_id]) const loginClicked = () => { posthog?.capture('clicked_log_in') login() } return (
{/* Fire a custom event when the button is clicked */} {/* This button click event is autocaptured by default */} {/* This button click event is not autocaptured */}
) } 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: React PostHog AI ```jsx useEffect(() => { posthog?.capture('test') // using optional chaining (recommended) if (posthog) { posthog.capture('test') // using an if statement } }, [posthog]) ``` 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:** React PostHog AI ```jsx import { PostHogCaptureOnViewed } from '@posthog/react' function App() { return (
Your important content here
) } ``` **With custom properties:** You can include additional properties with the event to provide more context: React PostHog AI ```jsx ``` **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: React PostHog AI ```jsx ``` 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`: React PostHog AI ```jsx