# Progress ring

> A circular progress ring that fills on a spring, laps itself past 100 % with a gap that lifts the lap off the ring, and bounces once with a success haptic when it completes.

- Type: Component (data)
- Access: Free, open source (MIT)
- Page: https://goodui.dev/native/components/progress-ring
- Markdown: https://goodui.dev/native/components/progress-ring.md
- Registry item: https://goodui.dev/native/r/progress-ring.json
- Dependencies: react-native-reanimated, react-native-svg
- Keywords: react native progress ring, circular progress, activity rings, radial progress bar, goal ring, svg progress circle, expo progress circle

## When to use

- Goals and quotas: steps, move minutes, savings targets, storage used, course completion.
- Several related goals at once: pass rings for activity-rings style concentric progress.
- Determinate tasks with a known fraction, like an upload, when the ring sits next to its label.

## When not to use

- Waiting without a known fraction: use the Spinner from the foundation, or a button that returns its promise.
- Placeholders while content loads: use skeleton.
- Trends over time: use sparkline.

## Installation

With the shadcn CLI (needs a components.json in the Expo app), then `npx expo install --fix` so native dependencies match your Expo SDK. The foundation comes along the first time.

```bash
npx shadcn@latest add https://goodui.dev/native/r/progress-ring.json
npx expo install --fix
```

The component lands in `components/goodui-native/progress-ring/` and imports the foundation from `components/goodui-native/foundation/` with relative paths; it also installs locale, rolling-number.

Built-in strings follow `<GooduiLocaleProvider>` (one [language pack](https://goodui.dev/native/r/locale-tr.json) translates the whole kit); the `labels` prop overrides them for one instance.

## Usage

```tsx
import { ProgressRing } from "@/components/goodui-native/progress-ring/progress-ring";
import { Text } from "@/components/goodui-native/foundation/text";

export function StepsGoal({ steps }: { steps: number }) {
  return (
    <ProgressRing value={steps / 10000} size="lg" accessibilityLabel="Steps">
      <Text size="title1" weight="medium" tabular>{steps.toLocaleString()}</Text>
      <Text size="footnote" tone="secondary">of 10,000 steps</Text>
    </ProgressRing>
  );
}
```

## Examples

### Activity rings

```tsx
<ProgressRing
  size="lg"
  rings={[
    { label: "Move", value: 1.35, tone: "danger" },
    { label: "Exercise", value: 0.72, tone: "success" },
    { label: "Stand", value: 0.5, tone: "accent" },
  ]}
/>
```

### Rolling percentage in the center

```tsx
<ProgressRing value={uploaded / total} showValue tone="success" accessibilityLabel="Upload" />
```

### Small, inline

```tsx
<ProgressRing value={0.4} size="sm" tone="warning" accessibilityLabel="Storage" />
```

## API reference

### ProgressRing

`import { ProgressRing } from "@/components/goodui-native/progress-ring/progress-ring";`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `number` | `0` | Progress of a single ring, 0 to 1. Above 1 the ring laps itself. Ignored when `rings` is set. |
| `rings` | `ProgressRingSpec[]` | – | Concentric rings, outermost first, activity-rings style. |
| `size` | `"sm" \| "md" \| "lg" \| number` | `"md"` | Diameter: `sm` 44, `md` 96, `lg` 168, or a number in points. |
| `thickness` | `number` | – | Stroke width. Defaults to about a twelfth of the diameter. |
| `tone` | `"success" \| "warning" \| "danger" \| "accent" \| "neutral"` | `"accent"` | Color of a single ring, and the default for `rings`. |
| `children` | `ReactNode` | – | Center content. Replaces `showValue`. |
| `showValue` | `boolean` | `false` | Show the first ring's percentage in the center as a rolling number. |
| `animateOnMount` | `boolean` | `true` | Draw the arcs in from zero when the ring mounts. Default `true`. |
| `haptic` | `HapticName \| false` | `"success"` | Haptic when a ring reaches 100 % after mount. `false` turns it off. |
| `on` | `"surface" \| "background"` | `"surface"` | The surface the ring sits on. The gap that separates a lap from the ring under it uses this color. |
| `accessibilityLabel` | `string` | – | Label for a single ring, for example "Upload". Rings use their own labels. Same as `labels.progress`. |
| `labels` | `Partial<ProgressRingLabels>` | – | Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. |
| `style` | `StyleProp<ViewStyle>` | – | Style for the outer container. |
| `testID` | `string` | – | Identifier for end-to-end tests. |

## Accessibility

- accessibilityRole is progressbar; a single ring reports accessibilityValue from 0 to 100.
- The label lists every ring with its percentage, for example 'Move 135 percent, Exercise 72 percent'.
- Reaching 100 % is announced as '<label> complete'.
- Center content is hidden from screen readers because the ring's label already carries the value.

## Motion

- Arcs fill on the smooth spring; on mount they draw in from zero, outer ring first, 90 ms apart.
- Past 100 % a lap arc draws on top, and a disc in the surface color under its head separates it from the ring below.
- Crossing 100 % after mount scales the ring to 1.06 and back on the bouncy spring, once.
- The center percentage rolls with rolling-number.
- Reduced motion: arcs jump to their value, there is no bounce, and the number changes without rolling.

## Haptics

- success when a ring crosses 100 % after mount (configurable with the haptic prop); never on mount

## Performance

- Arc length, lap and gap position are animated props on the UI thread; React renders only when values change.

## Notes for AI

- value is a fraction, not a percent: pass 0.72, not 72.
- Set on="background" when the ring sits on the screen background instead of a card, so the lap gap matches.
- For activity rings pass rings (outermost first) with a label each; value and accessibilityLabel are then ignored.
- Put your own text in children for units like 'of 10,000 steps'; showValue only renders the percentage.

## Related

- [Rolling number](https://goodui.dev/native/components/rolling-number.md): A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.
- [Sparkline](https://goodui.dev/native/components/sparkline.md): A small line or area chart that draws itself in and morphs to new data; press and drag to scrub, and a dot rides the curve while the line beyond your finger dims and every point ticks.
- [Skeleton](https://goodui.dev/native/components/skeleton.md): Loading placeholders whose shimmer is one light passing diagonally over the whole screen, plus presets and a Swap that eases from the skeleton's height to the content's while it cross-fades.

## Guidance for AI tools

Progress ring: A circular progress ring that fills on a spring, laps itself past 100 % with a gap that lifts the lap off the ring, and bounces once with a success haptic when it completes. Use only the documented props. Keep accessibility, reduced motion and both color schemes intact when adapting it.

Full library index: https://goodui.dev/native/llms.txt
