Use the React adapter as a client-side island — zero JS on initial render, hydrates on interaction.

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

Layout with Island

---
// src/layouts/Layout.astro
import { ProgressBar } from 'nprogress-next-react'
---
<html lang="en">
  <head>...</head>
  <body>
    <ProgressBar client:load />
    <slot />
  </body>
</html>

Component with useProgress

// src/components/ProgressDemo.tsx
'use client'
import { ProgressBar, useProgress } from 'nprogress-next-react'

export default function ProgressDemo() {
  const { status, start, done } = useProgress()
  return (
    <>
      <ProgressBar progress={status ?? 0} color="#29d" />
      <button onClick={() => { start(); setTimeout(done, 2000) }}>Start</button>
    </>
  )
}

Usage in Page

---
import ProgressDemo from '../components/ProgressDemo.tsx'
---
<ProgressDemo client:visible />

Vanilla CDN (Alternative)

Use the UMD global directly in a script — no React needed.

---
// src/pages/index.astro
---
<html lang="en">
  <head>
    <script src="https://unpkg.com/nprogress-next@latest" defer></script>
    <script>
      document.addEventListener('astro:after-swap', () => {
        NProgressNext.start()
        // simulate navigation
        setTimeout(() => NProgressNext.done(), 1500)
      })
    </script>
  </head>
  <body>
    <slot />
  </body>
</html>

Comparison

ApproachProsCons
React islandType-safe, reactive hooks, full React ecosystemAdds React to bundle (~40 KB)
Vanilla CDNZero deps, ≈ 3 KB, works everywhereImperative API, no reactivity

SSR Notes