Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 11 additions & 30 deletions contents/docs/integrate/_snippets/install-react.mdx
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
> For Next.js, we recommend following the [Next.js integration guide](/docs/integrate/next-js) instead.
> For React-based frameworks, we recommend the [Next.js integration guide](/docs/libraries/next-js) and [Next.js integration guide](/docs/libraries/remix) instead.

1. Install [`posthog-js`](https://github.com/posthog/posthog-js) and `@posthog/react` using your package manager:

import InstallReactPackageManagers from "./install-react-package-managers.mdx"

<InstallReactPackageManagers />

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 API key and host in [your project settings](https://us.posthog.com/settings/project). Including `VITE_PUBLIC_` in their names ensures they are accessible in the frontend.
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 API key and host in [your project settings](https://us.posthog.com/settings/project). If you're using Vite, include `VITE_PUBLIC_` in their names ensures they are accessible in the frontend.

```shell file=.env.local
VITE_PUBLIC_POSTHOG_KEY=<ph_project_api_key>
VITE_PUBLIC_POSTHOG_HOST=<ph_client_api_host>
```

3. Integrate PostHog at the root of your app (such as `main.jsx` if you are using Vite).
3. Integrate PostHog at the root of your app (such as `main.jsx` for Vite apps and `root.tsx` for React Router V7).

```react
// 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'
import posthog from 'posthog-js'; // +
import { PostHogProvider } from '@posthog/react' // +

posthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, {
api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST,
defaults: '<ph_posthog_js_defaults>',
});
posthog.init(import.meta.env.VITE_PUBLIC_POSTHOG_KEY, { // +
api_host: import.meta.env.VITE_PUBLIC_POSTHOG_HOST, // +
defaults: '<ph_posthog_js_defaults>', // +
}); // +

createRoot(document.getElementById('root')).render(
<StrictMode>
<PostHogProvider client={posthog}>
<PostHogProvider client={posthog}> // +
<App />
</PostHogProvider>
</PostHogProvider> // +
</StrictMode>,
)
```
Expand All @@ -45,22 +45,3 @@ import CalloutBox from 'components/Docs/CalloutBox'
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.

</CalloutBox>

<details>
<summary>Using React Router v7?</summary>

You need to set `posthog-js` and `@posthog/react` as external packages in your `vite.config.ts` file to avoid SSR errors.

```ts file=vite.config.ts
// ... imports

export default defineConfig({
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],
ssr: {
noExternal: ['posthog-js', '@posthog/react']
}
});
```

See our [Remix docs](/docs/libraries/remix) for more details.
</details>
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react'
import { DecisionTree } from 'components/Docs/DecisionTree'
import type { DecisionTreeQuestion, DecisionTreeRecommendation } from 'components/Docs/DecisionTree'

const questions: DecisionTreeQuestion[] = [
{
id: 'version',
question: 'Pick the right guide for you',
description: 'Check your package.json file for the react-router version.',
options: [
{ value: 'v7', label: '7.x.x (React Router V7)' },
{ value: 'v6', label: '6.x.x (React Router V6)' },
],
},
{
id: 'v7-mode',
question: 'Which React Router V7 mode are you using?',
description: 'How are your routes configured and defined?',
condition: (answers) => answers.version === 'v7',
options: [
{ value: 'framework', label: 'Using react-router.config.ts' },
{ value: 'data', label: 'Using <RouterProvider>' },
{ value: 'declarative', label: 'Using <BrowserRouter>' },
],
},
]

const getRecommendation = (answers: Record<string, string>): DecisionTreeRecommendation => {
if (answers.version === 'v6') {
return {
title: 'React Router V6',
path: '/docs/libraries/react-router/react-router-v6',
reason: 'You are using React Router V6. Follow the React Router V6 guide for setup instructions.',
}
}

if (answers['v7-mode'] === 'framework') {
return {
title: 'React Router V7 - Framework mode',
path: '/docs/libraries/react-router/react-router-v7-framework-mode',
reason: 'You are using React Router V7 in framework mode (Remix V3). This is the default mode and functions as an SSR framework.',
}
}

if (answers['v7-mode'] === 'data') {
return {
title: 'React Router V7 - Data mode',
path: '/docs/libraries/react-router/react-router-v7-data-mode',
reason: 'You are using React Router V7 in data mode. This mode is for building SPAs with APIs like loader, action, and useFetcher.',
}
}

if (answers['v7-mode'] === 'declarative') {
return {
title: 'React Router V7 - Declarative mode',
path: '/docs/libraries/react-router/react-router-v7-declarative-mode',
reason: 'You are using React Router V7 in declarative mode. This mode is for building SPAs with basic routing.',
}
}

return {
title: 'React Router setup',
path: '/docs/libraries/react-router',
reason: 'Follow the React Router docs to identify your version and mode.',
}
}

const ReactRouterDecisionTree: React.FC = () => {
return <DecisionTree questions={questions} getRecommendation={getRecommendation} />
}

export default ReactRouterDecisionTree
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<UsageWarning />

On the client-side, you can access the PostHog client using the `usePostHog` hook. This hook returns the initialized PostHog client, which you can use to call PostHog methods. For example:

```tsx
import { usePostHog } from '@posthog/react'

function App() {
const posthog = usePostHog()
return <button onClick={() => posthog?.capture('button_clicked')}>Click me</button>
}
```

For a complete list of available methods, see the [posthog-js documentation](/docs/libraries/js).

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
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 and host in [your project settings](https://us.posthog.com/settings/project). If you're using Vite, including `VITE_PUBLIC_` in their names ensures they are accessible in the frontend.

```shell file=.env.local
VITE_PUBLIC_POSTHOG_KEY=<ph_project_api_key>
VITE_PUBLIC_POSTHOG_HOST=<ph_client_api_host>
```

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Now 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 `identify` method on the PostHog client:

```tsx
export default function Login() {
const { user, login } = useAuth();
const posthog = usePostHog(); // +

const handleLogin = async (e: React.FormEvent) => {
// existing code to handle login...
const user = await login({ email, password });

posthog?.identify(user.email, // +
{ // +
email: user.email, // +
name: user.name, // +
} // +
); // +
posthog?.capture('user_logged_in'); // +
};

return (
<div>
{/* ... existing code ... */}
<button onClick={handleLogin}>Login</button>
</div>
);
}
```

PostHog 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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
First, you'll need to install [`posthog-js`](https://github.com/posthog/posthog-js) and `@posthog/react` using your package manager. These packages allow you to capture **client-side** events.

11 changes: 11 additions & 0 deletions contents/docs/libraries/react-router/_snippets/step-next-steps.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Now 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](/docs/libraries/js): Learn more about the PostHog Web SDK and how to use it on the client-side.
- [PostHog Node SDK docs](/docs/libraries/node): Learn more about the PostHog Node SDK and how to use it on the server-side.
- [Identify users](/docs/product-analytics/identify): Learn more about how to identify users in your app.
- [Group analytics](/docs/product-analytics/group-analytics): Learn more about how to use group analytics in your app.
- [PostHog AI](/docs/posthog-ai): After capturing events, use PostHog AI to help you understand your data and build insights.
- [Feature flags and experiments](/docs/libraries/react#feature-flags): Feature flag and experiment setup is the same as React. You can find more details in the React integration guide.

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
At 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](/docs/product-analytics/autocapture).

You can also try to capture a custom event to verify it's working. You can access PostHog in any component using the `usePostHog` hook.

```tsx
import { usePostHog } from '@posthog/react'

function App() {
const posthog = usePostHog()
return <button onClick={() => posthog?.capture('button_clicked')}>Click me</button>
}
```

You should see these events in a minute or two in the [activity tab](https://app.posthog.com/activity/explore).

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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
import { PostHogCaptureOnViewed } from '@posthog/react'

function App() {
return (
<PostHogCaptureOnViewed name="hero-banner">
<div>Your important content here</div>
</PostHogCaptureOnViewed>
)
}
```

**With custom properties:**

You can include additional properties with the event to provide more context:

```react
<PostHogCaptureOnViewed
name="product-card"
properties={{
product_id: '123',
category: 'electronics',
price: 299.99
}}
>
<ProductCard />
</PostHogCaptureOnViewed>
```

**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
<PostHogCaptureOnViewed
name="product-gallery"
properties={{ gallery_type: 'featured' }}
trackAllChildren
>
<ProductCard id="1" />
<ProductCard id="2" />
<ProductCard id="3" />
</PostHogCaptureOnViewed>
```

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
<PostHogCaptureOnViewed
name="footer"
observerOptions={{
threshold: 0.5, // Element is 50% visible
rootMargin: '0px'
}}
>
<Footer />
</PostHogCaptureOnViewed>
```

The component passes all other props to the wrapper `div`, so you can add styling, classes, or other HTML attributes as needed.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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
useEffect(() => {
posthog?.capture('test') // using optional chaining (recommended)

if (posthog) {
posthog.capture('test') // using an if statement
}
}, [posthog])
```

Typescript helps protect against these errors.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<CalloutBox icon="IconWarning" title="Don't directly import PostHog" type="caution">

When using React Router, you should not directly import `posthog` from `posthog-js` other than during initialization. Instead, use the `usePostHog` hook to access the PostHog client to ensure PostHog is initialized before use.

</CalloutBox>

57 changes: 57 additions & 0 deletions contents/docs/libraries/react-router/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: React Router
sidebarTitle: React Router
sidebar: Docs
showTitle: true
github: 'https://github.com/PostHog/posthog-js'
platformLogo: reactRouter
features:
eventCapture: true
userIdentification: true
autoCapture: true
sessionRecording: true
featureFlags: true
groupAnalytics: true
surveys: true
llmAnalytics: false
errorTracking: true
---

import Tab from "components/Tab"
import ReactInstall from '../../integrate/_snippets/install-react.mdx'
import ReactRouterDecisionTree from './_snippets/react-router-decision-tree'

This guide walks you through setting up PostHog for React Router. React Router V7 has [**three** distinct](https://reactrouter.com/start/modes) modes, and each requires a different setup. If you're still on React Router V6, we have a guide for that, too.

## Which version/mode am I using?

<ReactRouterDecisionTree />

<details>
<summary>Differentiating between React Router versions and modes</summary>

Here's are quick tips to help you figure out which version/mode you're using:

First, check your `package.json` file for the `react-router` version. If it's `7.x.x`, you're using React Router V7. If it's `6.x.x`, you're using React Router V6.

Then check your project to distinguish between the different modes:
- If you see a `react-router.config.ts` file, you're using React Router V7 in framework mode.
- If your router is configured like this: `<RouterProvider router={router} />` with router being a `createBrowserRouter` instance, you're using React Router V7 in data mode.
- Finally, if your router has `<Routes>` and `<Route>` elements, you're using React Router V7 in declarative mode.

Follow the [React Router docs](https://reactrouter.com/start/modes) for more details.

</details>

## React Router guides

React Router V7 has [**three** distinct](https://reactrouter.com/start/modes) modes, and each requires a different setup. Some modes require only the client-side React SDK, while others require **both** the client-side React SDK and the server-side Node SDK.

Follow the [React Router docs](https://reactrouter.com/start/modes) to find out which mode you're using, then follow the guide for that mode:

| Guide | Description |
|------|-------------|
| [V7 - Framework mode (Remix V3)](/docs/libraries/react-router/react-router-v7-framework-mode) | This is the default mode. In framework mode, React Router functions as an SSR (server-side rendering) framework. |
| [V7 - Declarative mode](/docs/libraries/react-router/react-router-v7-declarative-mode) | In declarative mode, you can build SPAs (single-page applications) with basic routing. |
| [V7 - Data mode](/docs/libraries/react-router/react-router-v7-data-mode) | Data mode is also for building SPAs, but comes with APIs like loader, action, and useFetcher. |
| [React Router V6](/docs/libraries/react-router/react-router-v6) | React Router V6 is for building SPAs and has features similar to React Router V7 in declarative or data mode. |
Loading