Tracking pageviews in single-page apps (SPA)
Jun 11, 2024
A single-page application (or SPA) dynamically loads content for new pages using JavaScript instead of loading new pages from the server. Ideally, this enables users to navigate around the app without waiting for new pages to load, providing a seamless user experience.
PostHog's JavaScript Web SDK automatically captures pageview events on page load. The problem with SPAs is that page loads don't happen beyond the initial one. This means user navigation in your SPA isn't tracked.
Luckily, you can opt-in to tracking this behavior using one of 2 options:
- Recommended: set
defaults: '<ph_posthog_js_defaults>'
when initializing PostHog to use the most recent defaults. - Manually set
capture_pageview: 'history_change'
.
This tutorial shows you how to follow the recommended approach for the most popular SPA frameworks like Next.js, Vue, Svelte, and Angular.
Prerequisite: Each of these requires you to have an app created and PostHog installed. To install the PostHog JavaScript Web SDK, run the following command for the package manager of your choice:
Terminal
yarn add posthog-js# ornpm install --save posthog-js# orpnpm add posthog-js
Tracking pageviews in Next.js (app router)
To add PostHog to your Next.js app use it to capture pageviews, we create a PostHogProvider
component in the app
folder, initialize PostHog with our project API key and host (from your project settings). PostHog will automatically capture pageviews if initialized with updated defaults.
// app/providers.js'use client'import posthog from 'posthog-js'import { PostHogProvider } from 'posthog-js/react'import { useEffect } from 'react'export function PHProvider({ children }) {useEffect(() => {posthog.init('<ph_project_api_key>', {api_host: 'https://us.i.posthog.com',defaults: '2025-05-24',})}, []);return <PostHogProvider client={posthog}>{children}</PostHogProvider>}
We import this and use it in the app/layout.js
file.
// app/layout.jsimport "./globals.css";import { PHProvider } from './providers'export default function RootLayout({ children }) {return (<html lang="en"><PHProvider><body>{children}</body></PHProvider></html>);}
Tracking pageviews in React Router v7
If you are using React Router, start by setting posthog-js
and posthog-js/react
as external packages in your vite.config.ts
file.
// ... importsexport default defineConfig({plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],ssr: {noExternal: ['posthog-js', 'posthog-js/react']}});
Next, create a providers.tsx
file in the app
folder. PostHog will automatically track pageviews if initialized with updated defaults.
// app/providers.tsximport posthog from 'posthog-js'import { PostHogProvider } from 'posthog-js/react'if (typeof window !== 'undefined') {posthog.init('<ph_project_api_key>', {api_host: 'https://us.i.posthog.com',defaults: "2025-05-24",})}export function PHProvider({ children }: { children: React.ReactNode }) {return <PostHogProvider client={posthog}>{children}</PostHogProvider>}
Finally, import the PHProvider
component in the app/root.tsx
file.
// app/root.tsx// ... importsimport { PHProvider } from "./provider";// ... links, meta, etc.export function Layout({ children }: { children: React.ReactNode }) {return (<html lang="en"><head><meta charSet="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><Meta /><Links /></head><body><PHProvider>{children}<ScrollRestoration /><Scripts /></PHProvider></body></html>);}// ... rest of the file
Tracking pageviews in React Router (v6 or below)
If you are using React Router v6 or below AKA react-router-dom
, start by adding the PostHogProvider
component in the app
folder.
// app/providers.js'use client'import posthog from 'posthog-js'import { PostHogProvider } from 'posthog-js/react'if (typeof window !== 'undefined') {posthog.init('<ph_project_api_key>', {api_host: 'https://us.i.posthog.com',defaults: '2025-05-24',})}export function PHProvider({ children }) {return <PostHogProvider client={posthog}>{children}</PostHogProvider>}
We then import this and use it in the app/layout.js
file.
import * as React from 'react';import { PHProvider } from './providers'function App() {return (<html lang="en"><PHProvider><body>{children}</body></PHProvider></html>);}
Tracking pageviews in Vue
After creating a Vue app and setting up the vue-router
, create a new folder in the src/components
named plugins
. In this folder, create a file named posthog.js
. If PostHog is initialized with updated defaults, it automatically tracks pageviews.
// src/plugins/posthog.jsimport posthog from "posthog-js";export default {install(app) {app.config.globalProperties.$posthog = posthog.init("<ph_project_api_key>", {api_host: "https://us.i.posthog.com",defaults: "2025-05-24",});},};
After this, you can add the plugin to the main.js
file and use it along with the router to capture pageviews afterEach
route change.
// src/main.jsimport { createApp } from 'vue'import App from './App.vue'import router from './router'import posthogPlugin from '../plugins/posthog';const app = createApp(App);app.use(posthogPlugin).use(router).mount('#app');
Tracking pageviews in Svelte
If you haven't already, start by creating a +layout.js
file for your Svelte app in your src/routes
folder. PostHog automatically tracks your pageviews once initialized with updated defaults.
// src/routes/+layout.jsimport posthog from 'posthog-js'import { browser } from '$app/environment';export const load = async () => {if (browser) {posthog.init('<ph_project_api_key>', {api_host: 'https://us.i.posthog.com',defaults: '2025-05-24',})}return};
Tracking pageviews in Angular
To start tracking pageviews in Angular, begin by initializing PostHog in src/main.ts
. PostHog automatically tracks your pageviews when initialized with updated defaults.
import { bootstrapApplication } from '@angular/platform-browser';import { appConfig } from './app/app.config';import { AppComponent } from './app/app.component';import posthog from 'posthog-js';posthog.init('<ph_project_api_key>', {api_host: 'https://us.i.posthog.com',defaults: "2025-05-24",});bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));
Further reading
- What to do after installing PostHog in 5 steps
- What engineers get wrong about analytics
- Complete guide to event tracking
Subscribe to our newsletter
Product for Engineers
Read by 60,000+ founders and builders
We'll share your email with Substack
Questions? Ask Max AI.
It's easier than reading through 638 pages of documentation