# @tanem/react-nprogress

> A React primitive for building slim progress bars.

Latest version **7.1.9** (published 2026-10-26) · MIT license · 0 weekly downloads

## Install

```sh
npm install @tanem/react-nprogress
pnpm add @tanem/react-nprogress
yarn add @tanem/react-nprogress
bun add @tanem/react-nprogress
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 7.1.9 |
| Published | 2026-10-26 |
| First published | 2018-10-20 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 0 |
| Unpacked size | 49.8 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 466 |
| Author | Tane Morgan |
| Maintainers | tanem |
| Keywords | animation, component, hook, hooks, javascript, loading, nprogress, progress, react, render-props, spinner, typescript |

## Links

- npm: https://www.npmjs.com/package/@tanem/react-nprogress
- Repository: https://github.com/tanem/react-nprogress
- Homepage: github:tanem/react-nprogress
- Issues: github:tanem/react-nprogress/issues
- npm.io page: https://npm.io/package/@tanem/react-nprogress

## Alternatives

- [mobx-react](https://npm.io/package/mobx-react.md) — 2.8M weekly downloads
- [rc-tree](https://npm.io/package/rc-tree.md) — 2.6M weekly downloads
- [@react-oauth/google](https://npm.io/package/@react-oauth/google.md) — 1.3M weekly downloads
- [@wagmi/connectors](https://npm.io/package/@wagmi/connectors.md) — 877.0K weekly downloads
- [vee-validate](https://npm.io/package/vee-validate.md) — 836.4K weekly downloads

## Recent versions

- 7.1.9 (latest) — 2026-10-26
- 4.0.0-beta.3 (beta) — 2021-12-21
- 7.1.8 — 2026-10-21
- 7.1.7 — 2026-10-19
- 7.1.6 — 2026-10-12
- 7.1.5 — 2026-10-05
- 7.1.4 — 2026-09-28
- 7.1.3 — 2026-08-31
- 7.1.2 — 2026-08-10
- 7.1.1 — 2026-08-08
- 7.1.0 — 2026-08-01
- 7.0.0 — 2026-07-31
- 6.0.4 — 2026-07-29
- 6.0.3 — 2026-03-09
- 6.0.2 — 2026-03-02
- … 222 more at https://npm.io/package/@tanem/react-nprogress/versions

## README

# react-nprogress

[![npm version](https://img.shields.io/npm/v/@tanem/react-nprogress.svg?style=flat-square)](https://www.npmjs.com/package/@tanem/react-nprogress)
[![build status](https://img.shields.io/github/actions/workflow/status/tanem/react-nprogress/ci.yml?style=flat-square)](https://github.com/tanem/react-nprogress/actions?query=workflow%3ACI)
[![coverage status](https://img.shields.io/codecov/c/github/tanem/react-nprogress.svg?style=flat-square)](https://codecov.io/gh/tanem/react-nprogress)
[![npm downloads](https://img.shields.io/npm/dm/@tanem/react-nprogress.svg?style=flat-square)](https://www.npmjs.com/package/@tanem/react-nprogress)
[![minzipped size](https://img.shields.io/bundlephobia/minzip/@tanem/react-nprogress?style=flat-square)](https://bundlephobia.com/result?p=@tanem/react-nprogress)

> A React primitive for building slim progress bars.

[Background](#background) | [When to Use This](#when-to-use-this) | [Usage](#usage) | [API](#api) | [Live Examples](#live-examples) | [Installation](#installation) | [Contributing](#contributing) | [License](#license)

## Background

This is a React port of [rstacruz](https://github.com/rstacruz)'s [`nprogress`](https://github.com/rstacruz/nprogress) module. It exposes an API that encapsulates the logic of `nprogress` and renders nothing, allowing consumers to implement their own rendering.

Two versions of `nprogress` are in circulation and they trickle differently. The 2014 npm release, `0.2.0`, adds a random amount of at most `0.02` every 800ms. The repository's master branch, never published to npm, adds tiered amounts every 200ms. This library mirrors master, so a side by side comparison against the npm package or the official demo page will show a different pace. The [`increment`](#increment) option covers the `0.2.0` pacing if you prefer the older feel.

## When to Use This

This package is a headless primitive. It renders no markup and ships no CSS, supplying only the pacing state: a `progress` value that trickles towards completion, an `isFinished` flag, and the `animationDuration` to transition with. The bar itself is yours to write.

- Use a drop-in bar such as [`nextjs-toploader`](https://github.com/TheSGJ/nextjs-toploader), [`next-nprogress-bar`](https://github.com/Skyleen77/next-nprogress-bar), or [`nprogress`](https://github.com/rstacruz/nprogress) itself when you want a styled bar wired up to your router with no rendering work.
- Use this package when you render the bar yourself, for example with design-system components or custom containers and spinners, and want only the trickle and completion logic handled for you.
- Use this package when you need several progress bars on one page, each tracking its own state.

## Usage

`Container`, `Bar` and `Spinner` are components you write: this package renders nothing itself. Every entry in [Live Examples](#live-examples) contains a working implementation of all three.

**Hook**

```jsx
import { useNProgress } from '@tanem/react-nprogress'

import Bar from './Bar'
import Container from './Container'
import Spinner from './Spinner'

const Progress = ({ isAnimating }) => {
  const { animationDuration, isFinished, progress } = useNProgress({
    isAnimating,
  })

  return (
    <Container animationDuration={animationDuration} isFinished={isFinished}>
      <Bar animationDuration={animationDuration} progress={progress} />
      <Spinner />
    </Container>
  )
}
```

**Render Props**

```jsx
import { NProgress } from '@tanem/react-nprogress'

import Bar from './Bar'
import Container from './Container'
import Spinner from './Spinner'

const Progress = ({ isAnimating }) => (
  <NProgress isAnimating={isAnimating}>
    {({ animationDuration, isFinished, progress }) => (
      <Container animationDuration={animationDuration} isFinished={isFinished}>
        <Bar animationDuration={animationDuration} progress={progress} />
        <Spinner />
      </Container>
    )}
  </NProgress>
)
```

**Restarting**

Both patterns leave the bar mounted between runs, and `progress` returns to `minimum` when it starts again. A bar that transitions `margin-left` or `transform` therefore animates backwards from where it finished, in full view, before it starts trickling forward. Back to back navigations hit this every time.

Change a `key` on the bar whenever it starts, so React mounts a fresh element at `minimum` instead:

```jsx
const [state, setState] = useState({ isAnimating: false, key: 0 })

const start = () => {
  setState((prevState) => ({ isAnimating: true, key: prevState.key ^ 1 }))
}

return <Progress isAnimating={state.isAnimating} key={state.key} />
```

Every entry in [Live Examples](#live-examples) does this. Dropping the transition while `isFinished` is not an alternative: `isFinished` is already `false` by the time `progress` resets, so the transition is back on for the step that moves the bar.

## API

The package exports one hook and one component. Both take the same [options](#options) and produce the same [values](#return-value), so the choice between them is a matter of which pattern suits the calling code. Both shapes are exported as types, for typing code that wraps either entry point:

```ts
import type { NProgressOptions, NProgressState } from '@tanem/react-nprogress'
```

### `useNProgress`

Returns the state of one progress bar. Call it once per bar: two calls, or two mounted `NProgress` components, track their progress independently.

```jsx
const { animationDuration, isFinished, progress } = useNProgress({
  animationDuration: 300,
  increment: (progress) => progress + 0.01,
  incrementDuration: 500,
  isAnimating: true,
  minimum: 0.1,
})
```

### `NProgress`

Takes the options as props and calls `children` with the values the hook returns. `children` is required and must return a React element.

```jsx
<NProgress
  animationDuration={300}
  increment={(progress) => progress + 0.01}
  incrementDuration={500}
  isAnimating
  minimum={0.1}
>
  {({ animationDuration, progress }) => (
    <Bar animationDuration={animationDuration} progress={progress} />
  )}
</NProgress>
```

### Options

All five options are optional. The type is `NProgressOptions`.

| Option                                    | Type                           | Default        |
| ----------------------------------------- | ------------------------------ | -------------- |
| [`animationDuration`](#animationduration) | `number`                       | `200`          |
| [`increment`](#increment)                 | `(progress: number) => number` | tiered trickle |
| [`incrementDuration`](#incrementduration) | `number`                       | `200`          |
| [`isAnimating`](#isanimating)             | `boolean`                      | `false`        |
| [`minimum`](#minimum)                     | `number`                       | `0.08`         |

#### `animationDuration`

Milliseconds the bar is given to animate out once it completes. `progress` reaches `1` as soon as `isAnimating` goes `false`, and `isFinished` follows this many milliseconds later, leaving that window for the exit transition. The value is also returned unchanged, so a single number drives both the timing and the CSS transitions.

#### `increment`

Size of each trickle step. The function is called with the current `progress` and returns the next value. The default is the tiered curve nprogress uses: `+0.1` below `0.2`, then `+0.04`, `+0.02`, and `+0.005` as `progress` grows, held at a ceiling of `0.994` so the bar never looks complete before it is.

The return value is clamped to between `minimum` and `1`, and nothing else. A custom function therefore owns its own ceiling. Leave it short of `1`, since reaching `1` is what completion means, and let `isAnimating` going `false` take the bar the rest of the way.

Returning a random amount is fine, but keep the function free of other side effects. It runs inside a React state update, and StrictMode calls it twice per increment in development. This trickles a random amount of at most `0.02` every 800ms, which is how nprogress `0.2.0` paces itself:

```jsx
const { progress } = useNProgress({
  increment: (progress) => Math.min(progress + Math.random() * 0.02, 0.994),
  incrementDuration: 800,
  isAnimating,
})
```

`0.2.0` also transitions the bar with `ease` where master uses `linear`. Easing lives in your renderer's CSS, so match it there if you want the rest of that look. The [Classic 0.2.0](https://github.com/tanem/react-nprogress/tree/master/examples/classic-020) example puts both together.

A new function identity on every render is fine too: passing an inline function does not restart the trickle timer. The next increment uses the latest function.

#### `incrementDuration`

Milliseconds between increments while the bar is animating. It controls the trickle pacing only. Step size is [`increment`](#increment).

#### `isAnimating`

Whether the bar is running. Going `true` starts it, going `false` completes it. Completion is what drives the final state: `progress` is set to `1`, and `isFinished` becomes `true` `animationDuration` milliseconds later.

#### `minimum`

Lower bound for `progress`, between `0` and `1`. The bar first appears at this value, then trickles up from there. Changing it while the bar is animating does not rewind the bar. Progress holds where it is, and the new bound applies from the next increment.

### Return Value

`useNProgress` returns these values, and `NProgress` passes the same object to `children`. The type is `NProgressState`.

| Value               | Type      | Description                                                                                                                                                   |
| ------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `animationDuration` | `number`  | The `animationDuration` option, passed through so rendering code can transition with it.                                                                      |
| `isFinished`        | `boolean` | `true` before the bar starts and again once it has animated out. `false` from when `isAnimating` goes `true` until `animationDuration` after it goes `false`. |
| `progress`          | `number`  | Starts at `0`, appears at `minimum` when the bar starts, then trickles up by [`increment`](#increment) and goes to `1` on completion.                         |

## Live Examples

| Example                                                                                                | Sandbox                                                                                                      |
| ------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ |
| [Classic 0.2.0](https://github.com/tanem/react-nprogress/tree/master/examples/classic-020)             | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/classic-020)        |
| [Material UI](https://github.com/tanem/react-nprogress/tree/master/examples/material-ui)               | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/material-ui)        |
| [Multiple Instances](https://github.com/tanem/react-nprogress/tree/master/examples/multiple-instances) | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/multiple-instances) |
| [Next App Router](https://github.com/tanem/react-nprogress/tree/master/examples/next-app-router)       | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/next-app-router)    |
| [Next Pages Router](https://github.com/tanem/react-nprogress/tree/master/examples/next-pages-router)   | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/next-pages-router)  |
| [Original Design](https://github.com/tanem/react-nprogress/tree/master/examples/original-design)       | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/original-design)    |
| [Plain JS](https://github.com/tanem/react-nprogress/tree/master/examples/plain-js)                     | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/plain-js)           |
| [React Router](https://github.com/tanem/react-nprogress/tree/master/examples/react-router)             | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/react-router)       |
| [Render Props](https://github.com/tanem/react-nprogress/tree/master/examples/render-props)             | [Open](https://codesandbox.io/p/devbox/github/tanem/react-nprogress/tree/master/examples/render-props)       |

## Installation

```
$ npm install @tanem/react-nprogress
```

## Contributing

Issues and pull requests are welcome. The development loop is `npm run test:src`, and `npm test` runs the full suite. Repository conventions, for humans and coding agents alike, live in [`AGENTS.md`](AGENTS.md).

## License

MIT

---
_Source: https://npm.io/package/@tanem/react-nprogress · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
