Agent skill
route-transition-tracking
Measure time from navigation to page fully loaded and interactive. Use when tracking SPA navigation, route changes, or slow page transitions.
Install this agent skill to your Project
npx add-skill https://github.com/nexus-labs-automation/web-observability/tree/main/skills/route-transition-tracking
SKILL.md
Route Transition Tracking
Time from navigation start to destination page interactive.
Phases
CLICK/NAV → ROUTE_CHANGE → DATA_FETCH → RENDER → INTERACTIVE
|_______________________________________________|
Route Transition Time
When to Use
- Tab/menu navigation
- Link clicks within SPA
- Programmatic navigation
- Back/forward browser buttons
- Deep links
Key Thresholds
| Rating | Duration |
|---|---|
| Good | <400ms |
| Acceptable | <1s |
| Poor | >1s |
React Router
import { useLocation, useNavigationType } from 'react-router-dom';
import { useEffect, useRef } from 'react';
function RouteTracker() {
const location = useLocation();
const navigationType = useNavigationType();
const navigationStart = useRef<number>();
useEffect(() => {
navigationStart.current = performance.now();
}, [location.pathname]);
useEffect(() => {
// Called after render completes
if (navigationStart.current) {
const duration = performance.now() - navigationStart.current;
trackRouteTransition({
from: document.referrer,
to: location.pathname,
duration_ms: duration,
navigation_type: navigationType, // 'PUSH', 'POP', 'REPLACE'
});
}
});
return null;
}
Next.js App Router
// app/providers.tsx
'use client';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect, useRef } from 'react';
export function RouteChangeTracker() {
const pathname = usePathname();
const searchParams = useSearchParams();
const startTime = useRef<number>();
const previousPath = useRef<string>();
useEffect(() => {
startTime.current = performance.now();
}, [pathname, searchParams]);
useEffect(() => {
if (startTime.current && previousPath.current) {
const duration = performance.now() - startTime.current;
trackRouteTransition({
from: previousPath.current,
to: pathname,
duration_ms: duration,
has_search_params: searchParams.toString().length > 0,
});
}
previousPath.current = pathname;
});
return null;
}
Vue Router
// plugins/route-tracking.ts
import { watch } from 'vue';
import { useRouter, useRoute } from 'vue-router';
export function useRouteTracking() {
const router = useRouter();
const route = useRoute();
let navigationStart: number;
router.beforeEach((to, from) => {
navigationStart = performance.now();
});
router.afterEach((to, from) => {
// Wait for next tick to ensure render complete
nextTick(() => {
const duration = performance.now() - navigationStart;
trackRouteTransition({
from: from.path,
to: to.path,
duration_ms: duration,
route_name: to.name as string,
});
});
});
}
SvelteKit
// src/routes/+layout.svelte
<script lang="ts">
import { page, navigating } from '$app/stores';
import { onMount } from 'svelte';
let navigationStart: number;
$: if ($navigating) {
navigationStart = performance.now();
}
$: if (!$navigating && navigationStart) {
const duration = performance.now() - navigationStart;
trackRouteTransition({
to: $page.url.pathname,
duration_ms: duration,
route_id: $page.route.id,
});
}
</script>
Nuxt
// plugins/route-tracking.client.ts
export default defineNuxtPlugin((nuxtApp) => {
let navigationStart: number;
nuxtApp.hook('page:start', () => {
navigationStart = performance.now();
});
nuxtApp.hook('page:finish', () => {
const route = useRoute();
const duration = performance.now() - navigationStart;
trackRouteTransition({
to: route.path,
duration_ms: duration,
route_name: route.name as string,
});
});
});
With Data Loading
Track data fetching as part of transition:
function ProductPage() {
const { data, isLoading } = useQuery(['product', id], fetchProduct);
const renderStart = useRef(performance.now());
useEffect(() => {
if (!isLoading && data) {
trackRouteTransition({
route: `/products/${id}`,
total_duration_ms: performance.now() - renderStart.current,
data_fetch_complete: true,
});
}
}, [isLoading, data]);
// ...
}
Common Mistakes
- Measuring only route change (missing data load)
- Not distinguishing warm vs cold loads
- Ignoring prefetched routes (artificially fast)
- Missing back/forward navigation
- Not tracking by route pattern (aggregate hides issues)
Related Skills
- See
skills/core-web-vitalsfor LCP during navigation - See
skills/hydration-performancefor SSR page loads - See
skills/api-tracingfor data fetch timing - See
skills/user-journey-trackingfor correlating with user intent
References
references/ui-performance.md- Navigation performance patternsreferences/frameworks/*.md- Framework-specific routing
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
user-journey-tracking
Track user journeys with intent context and friction signals. Use when instrumenting funnels or multi-step flows.
bundle-performance
Monitor JavaScript bundle size and execution performance. Use when tracking bundle size, identifying large chunks, or optimizing load performance.
hydration-performance
Measure SSR hydration timing and issues. Use when tracking hydration performance, debugging hydration mismatches, or optimizing SSR/SSG applications.
error-tracking
Set up error tracking with actionable context. Use when configuring error capture or error boundaries.
source-map-setup
Configure source maps for readable stack traces. Use when setting up error tracking, debugging production issues, or configuring build tools.
synthetic-monitoring
Set up synthetic monitoring with Lighthouse CI and Playwright. Use when implementing automated performance testing, CI/CD performance gates, or proactive monitoring.
Didn't find tool you were looking for?