navigation progress hook for Next.js App Router with real progress tracking
npm install use-navigation-progressš Production-ready navigation progress hook for Next.js App Router with real progress tracking.



* ā
Real Progress Tracking - No fake simulations, tracks actual navigation steps
* ā
Production Ready - Performance optimized with RAF, debouncing, and memory leak prevention
* ā
TypeScript First - Fully typed with excellent IntelliSense support
* ā
Flexible Configuration - Customizable steps, timeouts, and behaviors
* ā
Zero Dependencies - Only requires React and Next.js (peer dependencies)
``bash`
npm install use-navigation-progressor
yarn add use-navigation-progressor
pnpm add use-navigation-progress
`tsx
'use client';
import { useNavigationProgress } from 'use-navigation-progress';
export default function NavigationBar() {
const { status, progress } = useNavigationProgress();
if (status !== 'loading') return null;
return (
}}
/>
Advanced Usage
$3
`tsx
const progress = useNavigationProgress({
enableAutoComplete: false, // Manual control
timeout: 15000, // 15 second timeout
steps: [
{ name: "route_change", weight: 20 },
{ name: "auth_check", weight: 15 },
{ name: "data_fetch", weight: 40 },
{ name: "render_complete", weight: 25 }
]
});// In your component
useEffect(() => {
checkAuth()
.then(() => progress.markStepComplete('auth_check'))
.then(() => fetchData())
.then(() => progress.markStepComplete('data_fetch'))
.finally(() => progress.finish());
}, []);
`$3
`tsx
'use client';
import { useNavigationProgress } from 'use-navigation-progress';export function ProgressBar() {
const { status, progress, duration, error } = useNavigationProgress({
timeout: 8000,
debounceMs: 50
});
if (status === 'idle') return null;
return (
style={{
transform: scaleX(${progress / 100}),
opacity: status === 'complete' ? 0 : 1
}} /> {error && (
Navigation failed: {error}
)} {/ Optional: Show duration for debugging /}
{process.env.NODE_ENV === 'development' && (
{duration}ms - {progress}%
)}
);
}
`API Reference
$3
#### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
|
timeout | number | 10000 | Timeout in milliseconds before marking navigation as failed |
| steps | Array<{name: string, weight: number}> | Default steps | Custom navigation steps with weights |
| enableAutoComplete | boolean | true | Automatically complete progress when all steps are done |
| debounceMs | number | 100 | Debounce time in milliseconds for progress updates |#### Returns
| Property | Type | Description |
|----------|------|-------------|
|
status | 'idle' \| 'loading' \| 'complete' \| 'error' | Current navigation status |
| progress | number | Progress percentage (0-100) |
| duration | number | Navigation duration in milliseconds |
| error | string \| undefined | Error message if status is "error" |
| finish | () => void | Manually finish the navigation progress |
| markStepComplete | (stepName: string) => void | Mark a specific step as complete |
| reset | () => void | Reset progress to idle state |#### Default Steps
`typescript
[
{ name: "route_change", weight: 20 }, // Route change detected
{ name: "component_mount", weight: 30 }, // Component mounted
{ name: "hydration", weight: 25 }, // React hydration complete
{ name: "resources_load", weight: 25 } // Images and resources loaded
]
`Styling Examples
$3
`tsx
// Simple bar
fixed top-0 left-0 h-1 bg-blue-500 z-50 transition-all duration-200 ${
status === 'loading' ? 'opacity-100' : 'opacity-0'
}} style={{ width: ${progress}% }} />// Gradient bar with glow
style={{ width: ${progress}% }} />
`$3
`css
.progressBar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, #3b82f6, #8b5cf6, #ec4899);
transform-origin: left;
transition: transform 200ms ease-out;
z-index: 9999;
}.progressBar.complete {
opacity: 0;
transition: opacity 300ms ease-out;
}
`Best Practices
$3
`tsx
// app/layout.tsx
import { ProgressBar } from '@/components/progress-bar';export default function RootLayout({ children }) {
return (
{children}
);
}
`$3
`tsx
// For data-heavy pages
const { markStepComplete } = useNavigationProgress({
steps: [
{ name: "route_change", weight: 15 },
{ name: "auth_validation", weight: 20 },
{ name: "data_prefetch", weight: 35 },
{ name: "component_render", weight: 30 }
]
});
`$3
`tsx
const { status, error, reset } = useNavigationProgress();useEffect(() => {
if (status === 'error') {
console.error('Navigation failed:', error);
// Optionally reset after delay
setTimeout(reset, 3000);
}
}, [status, error, reset]);
``Demos
I've implemented a custom route announcer in slate with this package, you can implement your own top loader, custom route announcer etc with it
License
Ā© MIT