Progress
Linear determinate progress on bits-ui Progress. The indicator translates between 0 and max on a tokenized transition — no spin, no infinite loop.
Usage
40%
progress.svelte
<script lang="ts">
import { Progress as ProgressPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
max = 100,
value,
...restProps
}: WithoutChildrenOrChild<ProgressPrimitive.RootProps> = $props();
</script>
<ProgressPrimitive.Root
bind:ref
data-slot="progress"
class={cn(
'relative flex h-3 w-full items-center overflow-x-hidden rounded-(--radius-control) bg-muted',
className
)}
{value}
{max}
{...restProps}
>
<div
data-slot="progress-indicator"
class="size-full flex-1 bg-primary transition-transform duration-(--motion-duration-normal)"
style="transform: translateX(-{100 - (100 * (value ?? 0)) / (max ?? 1)}%)"
></div>
</ProgressPrimitive.Root>
Stepped values
Upload — 25%
Processing — 66%
Done — 100%
progress-steps.svelte
<script lang="ts">
import { Progress as ProgressPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
max = 100,
value,
...restProps
}: WithoutChildrenOrChild<ProgressPrimitive.RootProps> = $props();
</script>
<ProgressPrimitive.Root
bind:ref
data-slot="progress"
class={cn(
'relative flex h-3 w-full items-center overflow-x-hidden rounded-(--radius-control) bg-muted',
className
)}
{value}
{max}
{...restProps}
>
<div
data-slot="progress-indicator"
class="size-full flex-1 bg-primary transition-transform duration-(--motion-duration-normal)"
style="transform: translateX(-{100 - (100 * (value ?? 0)) / (max ?? 1)}%)"
></div>
</ProgressPrimitive.Root>
Indeterminate
Pass value= when progress is unknown — the indicator
parks at 0 and the surface stays visible.
progress-indeterminate.svelte
<Progress value={null} />With label
Pair the bar with a label row above it — keep the value tabular-nums so digits don't jitter as the count changes.
Uploading 40%
progress-with-label.svelte
<div class="grid gap-1.5">
<div class="flex items-center justify-between text-xs font-medium">
<span>Uploading</span>
<span class="tabular-nums text-muted-foreground">{value}%</span>
</div>
<Progress {value} />
</div>API reference
Inherits bits-ui Progress.Root props via spread. No children — the indicator is rendered internally.
| Prop | Type | Default | Description |
|---|---|---|---|
number | null | — | Current value between 0 and max. Null renders an indeterminate state. | |
number | 100 | Upper bound the value is normalized against. | |
string | — | Merged onto the track via tailwind-merge. | |
HTMLDivElement | null | null | Two-way-bindable element reference. |