Single Page Application Tracking
Single Page Applications (SPAs) present unique challenges for tracking because traditional pageview tracking relies on full page reloads. Learn how to implement proper tracking for SPAs built with React, Vue.js, Angular, and other modern frameworks.
The SPA Tracking Challenge
In traditional websites, each page navigation triggers a full page reload, making it easy to track pageviews. However, SPAs use client-side routing where:
- Page content changes dynamically without full reloads
- URLs change via JavaScript
pushState/replaceState - Navigation happens instantly through JavaScript
- Traditional tracking methods miss route changes
- Virtual pageviews are not automatically captured
❌ Without SPA Tracking:
- Only initial page load is tracked
- Route changes are invisible to analytics
- User journey appears incomplete
- Conversion attribution is broken
✅ With Proper SPA Tracking:
- All route changes are tracked as pageviews
- Complete user journey is captured
- Proper conversion attribution maintained
- Accurate engagement metrics
Konektor SPA Implementation
1. Basic SPA Tracking Setup
Initialize Konektor with SPA mode enabled:
// Enable SPA tracking in initialization
konektor('init', 'WS-YOUR-WORKSPACE-ID', {
spa: {
enabled: true,
autoTrack: true, // Automatically track route changes
trackHash: false // Set to true if using hash routing
}
});
2. Manual Route Change Tracking
For frameworks that need manual tracking, call the tracking function on route changes:
// Manual pageview tracking
function trackPageView() {
konektor('track', 'pageview', {
page: window.location.pathname,
title: document.title,
referrer: document.referrer
});
}
// Call on every route change
trackPageView();
Framework-Specific Implementation
React with React Router
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
function App() {
const location = useLocation();
useEffect(() => {
// Track pageview on route change
if (window.konektor) {
window.konektor('track', 'pageview', {
page: location.pathname,
search: location.search,
title: document.title
});
}
}, [location]);
return <YourApp />;
}
Vue.js with Vue Router
// In main.js or router.js
import router from './router';
router.afterEach((to, from) => {
// Track pageview after route change
if (window.konektor) {
window.konektor('track', 'pageview', {
page: to.path,
title: to.meta?.title || document.title,
referrer: from.path
});
}
});
Angular with Router
import { NavigationEnd, Router } from '@angular/router';
import { filter } from 'rxjs/operators';
export class AppComponent {
constructor(private router: Router) {
this.router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: NavigationEnd) => {
// Track pageview on navigation end
if (window.konektor) {
window.konektor('track', 'pageview', {
page: event.url,
title: document.title,
referrer: event.urlAfterRedirects
});
}
});
}
}
Next.js (Hybrid SPA/SSR)
// In _app.js
import { useRouter } from 'next/router';
import { useEffect } from 'react';
function MyApp({ Component, pageProps }) {
const router = useRouter();
useEffect(() => {
const handleRouteChange = (url) => {
// Track client-side route changes
if (window.konektor) {
window.konektor('track', 'pageview', {
page: url,
title: document.title
});
}
};
router.events.on('routeChangeComplete', handleRouteChange);
return () => {
router.events.off('routeChangeComplete', handleRouteChange);
};
}, [router.events]);
return <Component {...pageProps} />;
}
Advanced SPA Tracking Features
Virtual Pageview Tracking
Track dynamic content changes as virtual pageviews:
// Track modal opens as virtual pages
function trackModalView(modalName) {
konektor('track', 'pageview', {
page: `/virtual/modal/${modalName}`,
title: `Modal: ${modalName}`,
virtual: true
});
}
// Track tab changes
function trackTabChange(tabName) {
konektor('track', 'pageview', {
page: `${window.location.pathname}#${tabName}`,
title: `${document.title} - ${tabName}`,
virtual: true
});
}
E-commerce SPA Tracking
For SPAs with e-commerce functionality:
// Track product views
function trackProductView(product) {
konektor('track', 'view_item', {
content_name: product.name,
content_ids: [product.id],
content_type: 'product',
value: product.price,
currency: 'IDR'
});
}
// Track add to cart
function trackAddToCart(product, quantity) {
konektor('track', 'add_to_cart', {
content_name: product.name,
content_ids: [product.id],
content_type: 'product',
value: product.price * quantity,
currency: 'IDR',
quantity: quantity
});
}
User Authentication Tracking
Track login/logout events in SPAs:
// Track user login
function trackLogin(userId, method) {
konektor('identify', userId);
konektor('track', 'login', {
method: method,
user_id: userId
});
}
// Track user logout
function trackLogout() {
konektor('track', 'logout');
konektor('reset'); // Clear user identity
}
Testing SPA Tracking
1. Manual Testing
- Navigate through your SPA using different routes
- Check browser network tab for tracking requests
- Verify events are sent to Konektor dashboard
- Test back/forward browser buttons
- Validate direct URL access tracking
2. Automated Testing
Create a testing script to verify SPA tracking:
// SPA Tracking Test Script
function testSPATracking() {
const routes = ['/', '/about', '/products', '/contact'];
routes.forEach(route => {
// Simulate navigation
window.history.pushState({}, '', route);
// Trigger route change event
window.dispatchEvent(new Event('popstate'));
// Check if tracking was called
console.log(`Testing route: ${route}`);
});
}
testSPATracking();
3. Monitoring Tools
Use browser extensions to monitor tracking:
- Konektor Debug Tool: Monitor tracking calls
- Network Tab: Inspect tracking requests
- Console Logs: Check for tracking errors
Common SPA Tracking Issues
Route Changes Not Tracked
Symptoms: Only initial page load tracked, route changes missed Solutions:
- Ensure route change listeners are properly attached
- Check that tracking calls are not blocked by errors
- Verify framework-specific integration is correct
Duplicate Pageviews
Symptoms: Same page tracked multiple times Solutions:
- Implement proper debouncing for route changes
- Check for multiple route listeners
- Use navigation guards to prevent duplicate tracking
Tracking Fires Too Early
Symptoms: Page content not loaded when tracking fires Solutions:
- Delay tracking until content is ready
- Use
nextTickorsetTimeoutfor Vue/React - Wait for DOM updates before tracking
History API Issues
Symptoms: Browser back/forward not tracked properly Solutions:
- Listen to
popstateevents for browser navigation - Implement proper history change detection
- Test all navigation scenarios
Performance Optimization
Efficient Tracking Implementation
// Debounced tracking to prevent spam
let trackingTimeout;
function debouncedTrack(event, data) {
clearTimeout(trackingTimeout);
trackingTimeout = setTimeout(() => {
konektor('track', event, data);
}, 100);
}
// Use in route changes
router.afterEach(() => {
debouncedTrack('pageview', {
page: window.location.pathname,
title: document.title
});
});
Lazy Loading Considerations
For SPAs with lazy-loaded routes:
// Track lazy-loaded components
const LazyComponent = lazy(() => import('./MyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
// Track in lazy component
useEffect(() => {
konektor('track', 'component_load', {
component: 'MyComponent'
});
}, []);
Best Practices
1. Consistent Implementation
- Use the same tracking pattern across all routes
- Maintain consistent event naming conventions
- Standardize data structure for all events
2. Error Handling
- Implement try-catch blocks around tracking calls
- Gracefully handle cases where tracking fails
- Log tracking errors for debugging
3. Privacy Compliance
- Respect user consent for tracking
- Implement proper data sanitization
- Comply with privacy regulations (GDPR, CCPA)
4. Performance Monitoring
- Monitor tracking call response times
- Track tracking failure rates
- Optimize bundle size impact
Framework Comparison
| Framework | Integration Method | Complexity | Best For |
|---|---|---|---|
| React | useEffect + router events | Medium | Component-based apps |
| Vue.js | Router navigation guards | Easy | Progressive enhancement |
| Angular | Router events observable | Medium | Enterprise applications |
| Next.js | Router events | Easy | Hybrid SSR/SPA apps |
| Nuxt.js | Router middleware | Easy | Vue ecosystem |
Support Resources
Documentation
Getting Help
- Technical Support: support@konektor.id
- Framework Examples: GitHub repository
- Community Forum: Discuss with other developers
:::info Pro Tip Start with basic route tracking, test thoroughly, then add virtual pageviews and advanced e-commerce tracking as needed. :::
:::warning Important SPA tracking requires testing all navigation scenarios - direct URL access, browser navigation, and programmatic routing. :::
Need More Help?
Our team is ready to help you maximize ad tracking and business attribution.
© 2026 Konektor. All rights reserved.
