# Route Progress

> A React route progress bar: the thin bar at the top of the app while a page loads, started by in-app links or by hand for any async work.

- Type: React component (Feedback)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add route-progress`
- Docs: https://wingo-ui.com/components/route-progress

## Interaction

Click a link inside the LinkProvider and the bar slides in from the start edge after a short delay, creeps toward 90% while the next page loads, then fills to the end, holds for a beat and fades out once the path changes. Navigations faster than the delay never show it, a click on the page you are already on does nothing, and browser back / forward shows nothing either. Call routeProgress.start() and routeProgress.done() from anywhere (saving, exporting) to drive it by hand, or pass loading. It is never focusable and has nothing to tap; on a phone it sits under the status bar through the safe-area inset. Screen readers get a labeled progressbar with its value while it is visible and nothing while idle. Under reduced motion the bar jumps between values without springs or glow.

## Installation

```bash
npx wingo-ui@latest add route-progress
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/route-progress` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/route-progress.tsx`

npm dependencies: `npm i motion`

Wingo UI dependencies (installed automatically):

- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
"use client"

import type { ReactNode } from "react"
import Link from "next/link"
import { usePathname, useRouter } from "next/navigation"
import { Button } from "@/components/ui/button"
import { RouteProgress, routeProgress } from "@/components/ui/route-progress"
import { LinkProvider } from "@/lib/navigation"

// once, in the root layout's client providers: every NavLink click starts the bar, the new path finishes it
export function Providers({ children }: { children: ReactNode }) {
  const router = useRouter()
  return (
    <LinkProvider component={Link} currentPath={usePathname()} navigate={router.push}>
      <RouteProgress />
      {children}
    </LinkProvider>
  )
}

// by hand, for any async work
export function SaveInvoice({ save }: { save: () => Promise<void> }) {
  return (
    <Button
      onClick={async () => {
        routeProgress.start()
        try {
          await save()
        } finally {
          routeProgress.done()
        }
      }}
    >
      Save invoice
    </Button>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `auto?` | `boolean` | `true` | Starts on in-app navigations (a NavLink click, useNavigate) and finishes when the current path changes. A click on the current page, a hash or a query change does not count. |
| `loading?` | `boolean` |  | Controlled mode: true starts, false finishes. It overrides auto. Leave it undefined to use auto and the imperative routeProgress API. Docs-only: the Controlled loading example drives it from a fake request. |
| `currentPath?` | `string` |  | The path whose change finishes a navigation. Defaults to the LinkProvider's currentPath, which is how the playground feeds it. |
| `delay?` | `number` | `120` | Milliseconds before the bar appears. Anything that finishes sooner never shows it, so fast pages do not flash. |
| `minimum?` | `number` | `0.08` | Where the bar starts, as a fraction of the width. |
| `trickle?` | `boolean` | `true` | Creeps toward 90% while waiting; each step covers 5 to 15% of the distance left. |
| `trickleSpeed?` | `number` | `250` | Milliseconds between trickle steps. |
| `timeout?` | `number` | `10000` | Milliseconds after which a navigation that never finished completes anyway (a cancelled route, a redirect back to the same page). 0 turns the safety net off. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Bar height: 2 / 3 / 4px. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The bar's color from the design tokens. neutral is black in light mode and white in dark mode. |
| `color?` | `string` | `""` | Any CSS color. Replaces the tone for the bar, the glow and the spinner. Leave empty to use the tone. |
| `glow?` | `boolean` | `true` | A soft glow on the last 96px of the bar, at 60% opacity. Dropped under reduced motion. |
| `spinner?` | `boolean` | `false` | A 14px ring spinner in a small floating chip under the top end of the bar while loading. |
| `position?` | `"fixed" \| "absolute"` | `"fixed"` | fixed pins it to the top of the viewport; absolute pins it to the nearest positioned ancestor (a card, a panel, a demo frame). |
| `offset?` | `number` | `0` | Pixels from the top, e.g. 56 to sit under a fixed header. |
| `safeArea?` | `boolean` | `true` | Adds env(safe-area-inset-top) to the offset, so an installed web app draws the bar under the status bar. It is 0 in desktop browsers. |
| `label?` | `string` | `"Loading page"` | The progressbar's aria-label. |
| `store?` | `RouteProgressStore` | `"routeProgress"` | The store this bar shows. Defaults to the shared routeProgress; pass createRouteProgress() when two bars must not share state. |
| `classNames?` | `Partial<Record<"track" \| "bar" \| "glow" \| "spinner", string>>` |  | Extra classes for the inner parts. |
| `className?` | `string` |  | Extra classes for the root, merged last. Override the tone vars here, e.g. [--tone:var(--info)]. |

## Parts

### routeProgress (constant)

The shared imperative store. start() shows the bar after the delay (ignored while running), done() finishes it (silently while still pending), set() jumps to a fraction, inc() moves it forward, reset() hides it at once. Type: { start(); done(); set(fraction: number); inc(amount?: number); reset() }.

### useRouteProgress (hook)

The store's state and value, for a custom progress UI (a spinner in the tab title, a disabled form). Signature: (store?: RouteProgressStore) => { state: "idle" | "pending" | "loading" | "done"; value: number }.

### createRouteProgress (function)

Makes an independent store with the same API as routeProgress. Signature: (config?: Partial<RouteProgressConfig>) => RouteProgressStore.

## Examples

- **Tones and a custom color**: Every tone from the design tokens plus any CSS color; brand fits a marketing site.
- **Sizes, glow and spinner**: 2 / 3 / 4px bars, with and without the leading-edge glow, and the optional spinner.
- **Delay hides fast navigations**: Both links start the bar; the 80ms page arrives before the 120ms delay, so only the slow one shows it.
- **Controlled loading**: loading follows a fake fetch: true starts the bar, false finishes it.
- **Under a fixed header**: offset={56} places the bar under a 56px top bar.
- **Usage with Next**: LinkProvider with next/link and usePathname, plus one RouteProgress.

## See also

- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [Progress](https://wingo-ui.com/components/progress) ([Markdown](https://wingo-ui.com/components/progress.md))
- [Scroll Progress](https://wingo-ui.com/components/scroll-progress) ([Markdown](https://wingo-ui.com/components/scroll-progress.md))
- [Spinner](https://wingo-ui.com/components/spinner) ([Markdown](https://wingo-ui.com/components/spinner.md))
- [Toast](https://wingo-ui.com/components/toast) ([Markdown](https://wingo-ui.com/components/toast.md))

---

Canonical: https://wingo-ui.com/components/route-progress
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
