Skip to content

r2hu1/use-navigation-progress

Repository files navigation

use-navigation-progress

🚀 Production-ready navigation progress hook for Next.js App Router with real progress tracking.

npm version License: MIT TypeScript

Features

  • 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)

Installation

npm install use-navigation-progress
# or
yarn add use-navigation-progress
# or
pnpm add use-navigation-progress

Quick Start

'use client';
import { useNavigationProgress } from 'use-navigation-progress';

export default function NavigationBar() {
  const { status, progress } = useNavigationProgress();

  if (status !== 'loading') return null;

  return (
    <div className="fixed top-0 left-0 w-full h-1 bg-gray-200 z-50">
      <div
        className="h-full bg-blue-500 transition-all duration-200 ease-out"
        style={{ width: `${progress}%` }}
      />
    </div>
  );
}

Advanced Usage

Custom Steps & Manual Control

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());
}, []);

Complete Progress Bar Component

'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 (
    <div className="fixed top-0 left-0 w-full z-50">
      <div className="h-1 bg-gradient-to-r from-blue-500 to-purple-600 origin-left transform transition-transform duration-200 ease-out"
           style={{
             transform: `scaleX(${progress / 100})`,
             opacity: status === 'complete' ? 0 : 1
           }} />

      {error && (
        <div className="bg-red-500 text-white text-sm px-4 py-1">
          Navigation failed: {error}
        </div>
      )}

      {/* Optional: Show duration for debugging */}
      {process.env.NODE_ENV === 'development' && (
        <div className="text-xs text-gray-500 px-2">
          {duration}ms - {progress}%
        </div>
      )}
    </div>
  );
}

API Reference

useNavigationProgress(options?)

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

[
  { 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

Tailwind CSS

// Simple bar
<div className={`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
<div className="fixed top-0 left-0 w-full h-1 bg-gray-200 z-50">
  <div className="h-full bg-gradient-to-r from-blue-400 via-purple-500 to-pink-500 shadow-lg transition-all duration-300 ease-out"
       style={{ width: `${progress}%` }} />
</div>

CSS Modules

.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

1. Single Progress Bar Per App

// app/layout.tsx
import { ProgressBar } from '@/components/progress-bar';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ProgressBar />
        {children}
      </body>
    </html>
  );
}

2. Custom Loading Steps

// 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. Error Handling

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

About

build your own custom Next.js route announcer (page navigation loading comp) with full control

Resources

License

Stars

Watchers

Forks

Packages

No packages published