# Progress

> A linear progress bar for uploads, deployments and multi-step tasks.

Category: Feedback · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/progress

Use Progress when an operation takes more than a couple of seconds and you can measure it. Omit value for indeterminate work. Prefer formatted value text people understand, such as "12 of 48 users", over bare percentages.

## Import

```tsx
import { Progress } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Basic

```tsx
import { Progress } from '@bindshq/stance-react';

export function Basic() {
  return <Progress label="Uploading contract.pdf" value={64} showValue />;
}
```

### Custom value text

```tsx
import { Progress } from '@bindshq/stance-react';

export function ValueText() {
  return (
    <Progress
      label="Syncing users from Azure AD"
      value={12}
      max={48}
      showValue
      formatValue={(v, m) => `${v} of ${m} users`}
    />
  );
}
```

### Tones and sizes

```tsx
import { Progress } from '@bindshq/stance-react';

export function Tones() {
  return (
    <div style={{ display: 'grid', gap: 16 }}>
      <Progress label="Storage" value={42} size="sm" showValue />
      <Progress label="Mailboxes updated" value={100} tone="success" showValue />
      <Progress
        label="Seat usage"
        value={46}
        max={50}
        tone="warning"
        size="lg"
        showValue
        formatValue={(v, m) => `${v}/${m} seats`}
      />
      <Progress label="Deliveries failed" value={8} tone="danger" showValue />
    </div>
  );
}
```

### Indeterminate

```tsx
import { Progress } from '@bindshq/stance-react';

export function Indeterminate() {
  return <Progress label="Connecting to Exchange relay" />;
}
```

### Live update

```tsx
import * as React from 'react';
import { Button, Progress } from '@bindshq/stance-react';

export function Live() {
  const [value, setValue] = React.useState(0);
  React.useEffect(() => {
    if (value >= 100) return;
    const t = setTimeout(() => setValue((v) => Math.min(100, v + 7)), 300);
    return () => clearTimeout(t);
  }, [value]);
  return (
    <div style={{ display: 'grid', gap: 12 }}>
      <Progress
        label="Deploying Sales 2026"
        value={value}
        showValue
        tone={value >= 100 ? 'success' : 'default'}
      />
      <div>
        <Button size="sm" variant="outline" onClick={() => setValue(0)}>
          Restart
        </Button>
      </div>
    </div>
  );
}
```

## API reference

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| Progress | `value` | `number \| null` | None | Current value; null or omitted is indeterminate. |
| Progress | `max` | `number` | `100` | Maximum value. |
| Progress | `label` | `ReactNode` | None | Visible label and accessible name. |
| Progress | `showValue` | `boolean` | `false` | Show the formatted value. |
| Progress | `formatValue` | `(value: number, max: number) => string` | `percentage` | Display text and aria-valuetext. |
| Progress | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Track height 4, 8 or 12px. |
| Progress | `tone` | `'default' \| 'success' \| 'warning' \| 'danger'` | `'default'` | Indicator colour. |

## Accessibility

- Uses role="progressbar" with aria-valuemin, aria-valuemax, aria-valuenow and aria-valuetext.
- Indeterminate bars omit aria-valuenow, as the ARIA spec requires.
- Named by its visible label; pass aria-label when there is none.
- Indeterminate motion stops for people who prefer reduced motion.

## Guidelines

**Do**

- Announce completion separately, e.g. with a Toast.

**Don’t**

- Use progress bars for scores or capacity; use a meter-style StatCard instead.

## Related

- https://stanceui.com/components/progress-circle
- https://stanceui.com/components/skeleton
- https://stanceui.com/components/loading-overlay
