Installation

pnpm add nprogress-next-react
# peer: react >= 18

Components

<ProgressBar />

Controlled, presentational progress bar. Works with zero CSS imports (inline styles).

import { ProgressBar } from 'nprogress-next-react'

// Basic usage with singleton
<ProgressBar color="#29d" />

// Controlled with useProgress()
const { status } = useProgress()
<ProgressBar progress={status ?? 0} /> // progress: 0..1
PropTypeDefaultDescription
progressnumber0Progress value 0..1 (clamped)
visiblebooleantrueRender nothing when false
spinnerbooleantrueShow/hide spinner
labelstring | null'Loading'aria-label (null omits)
speednumber200Transition duration (ms)
easingstring'linear'CSS easing
heightstring'2px'Sets --np-height
colorstring'#29d'Sets --np-color
zIndexnumber | string1031Sets --np-z
classNamestring—Additional class
styleCSSProperties—Additional inline styles

Rendered DOM

<div class="np-root" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="42" aria-label="Loading" style="--np-color: #29d; --np-height: 2px; --np-z: 1031;">
  <div class="np-bar" style="transform: translate3d(-58%,0,0); transition: all 200ms linear;">
    <div class="np-peg"></div>
  </div>
  <div class="np-spinner" aria-hidden="true">
    <div class="np-spinner-icon"></div>
  </div>
</div>

useProgress(options?)

Hook that creates/returns a core instance and reactive state. Uses useSyncExternalStore for concurrent-safe subscriptions.

import { useProgress } from 'nprogress-next-react'

function MyComponent() {
  const { progress, status, isStarted, isRendered, start, done, set, inc } = useProgress({
    minimum: 0.1,
    trickle: true
  })

  return (
    <button onClick={() => { start(); setTimeout(() => done(), 2000) }}>
      Start Task
    </button>
  )
}
ReturnTypeDescription
progressProgressInstanceCore instance (createProgress())
statusnumber | nullReactive progress 0..1 or null
isStartedbooleanReactive progress.isStarted()
isRenderedbooleanReactive progress.isRendered()
start()voidStart progress
done(force?)voidComplete progress
set(n)voidSet progress 0..1
inc(amount?)voidIncrement

Complete Example

import { ProgressBar, useProgress } from 'nprogress-next-react'
import 'nprogress-next-react/styles.css' // optional

export default function App() {
  const { status, start, done } = useProgress()

  const handleFetch = async () => {
    start()
    try {
      await fetch('/api/data')
    } finally {
      done()
    }
  }

  return (
    <>
      <ProgressBar progress={status ?? 0} color="#29d" />
      <button onClick={handleFetch} disabled={status !== null}>
        {status !== null ? 'Loading...' : 'Fetch Data'}
      </button>
    </>
  )
}

Next.js App Router

// app/layout.tsx
import { ProgressBar } from 'nprogress-next-react'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <ProgressBar />
        {children}
      </body>
    </html>
  )
}

// In any component
import { useProgress } from 'nprogress-next-react'

export function MyComponent() {
  const { start, done } = useProgress()
  // ...
}

SSR Notes