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.
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/route-transition-tracking-nexus-labs-automatio-web-observability
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
typescript
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
typescript
// 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
typescript
// 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
typescript
// 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
typescript
// 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:
typescript
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
Didn't find tool you were looking for?