javascriptreactjstypescriptreact-router

How to use Posthog to capture pageview events with React Router?


I have the following SPA that uses react router:

ReactDOM.createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Providers>
      <BrowserRouter>
        <Routes>
          <Route element={<ProtectedRoutes />}>
            <Route path="/dashboard" element={<MainPage />} />
            <Route path="/dashboard/setup" element={<SetupPage />} />
            <Route path="/dashboard/project/:projectId" element={<ProjectPage />} />
          </Route>
          <Route path="/admin" element={<ProtectedRoutes admin />}>
            <Route path="" element={<div>you are admin</div>} />
            <Route path="users" element={<UsersPage />} />
            <Route path="data/view" element={<DataViewPage />} />
            <Route path="tasks" element={<TasksPage />} />
          </Route>
          <Route element={<PublicOnlyRoutes redirectTo="/dashboard?redirect=true" />}>
            <Route path="/login" element={<LoginPage />} />
          </Route>
          <Route path="/" element={<HomePage />} />
          <Route
            path="/demo"
            element={<DemoComponent message="Sample message" name="User" />}
          />
        </Routes>
      </BrowserRouter>
    </Providers>
  </StrictMode>,
);

I would like to use Posthog's event capturing with this to track when users navigate around the application. Like this:

function PostHogIdentifier() {
  const location = useLocation();

  // Track pageviews
  useEffect(() => {
    if (posthog) {
      console.log("Capturing pageview", window.location.href);
      posthog.capture(
        '$pageview',
        {
          '$current_url': window.location.href,
        }
      )
    }
  }, [location])
  
  return null;
}

But, I can't do this because the useLocation hook must be run inside of the BrowserRouter. And I'm not sure how to do this since the BrowserRouter only supports Route and Routes child components.


Solution

  • But, I can't do this because the useLocation hook must be run inside of the BrowserRouter.

    This is true, the useLocation hook can only be called in a component rendered within routing context.

    And I'm not sure how to do this since the BrowserRouter only supports Route and Routes child components.

    This is incorrect, the BrowserRouter can render anything as children, it is the Route and React.Fragment components that are the only valid children of the Routes component.

    PostHogIdentifier only needs to be rendered within the ReactTree created by BrowserRouter, i.e. it only needs to be rendered as a descendent.

    Example:

    ReactDOM.createRoot(document.getElementById("root")!).render(
      <StrictMode>
        <Providers>
          <BrowserRouter>
            <PostHogIdentifier /> {/* <-- rendered within routing context */}
    
            <Routes>
              <Route element={<ProtectedRoutes />}>
                <Route path="/dashboard" element={<MainPage />} />
                <Route path="/dashboard/setup" element={<SetupPage />} />
                <Route
                  path="/dashboard/project/:projectId"
                  element={<ProjectPage />}
                />
              </Route>
              <Route path="/admin" element={<ProtectedRoutes admin />}>
                <Route path="" element={<div>you are admin</div>} />
                <Route path="users" element={<UsersPage />} />
                <Route path="data/view" element={<DataViewPage />} />
                <Route path="tasks" element={<TasksPage />} />
              </Route>
              <Route
                element={
                  <PublicOnlyRoutes redirectTo="/dashboard?redirect=true" />
                }
              >
                <Route path="/login" element={<LoginPage />} />
              </Route>
              <Route path="/" element={<HomePage />} />
              <Route
                path="/demo"
                element={<DemoComponent message="Sample message" name="User" />}
              />
            </Routes>
          </BrowserRouter>
        </Providers>
      </StrictMode>,
    );