Thanks to visit codestin.com
Credit goes to docs.wirekit.app

Skip to main content
WireKit
Copy for LLM

Progress

A horizontal progress bar for displaying completion percentage (determinate) or indicating ongoing work without a known total (indeterminate).

Basic Usage

Determinate (known value)

Determinate progress at 42%
Upload Progress

Indeterminate (unknown duration)

Omit value to render an animated sliding bar for tasks with unknown duration.

Indeterminate — sliding animation
Processing…

Show Numeric Value

With current / max readout
Storage 2145 / 5000

Renders: 2145 / 5000 next to the label.

Variants

The fill color signals semantic meaning:

The four most-used intents
Accent — default
Success — task complete
Warning — approaching limit
Danger — quota full

Sizes

Three size variants
Small (h-1)
Medium (h-2) — default
Large (h-3)

Radial (circular) progress

For a radial progress indicator — the ring shape for dashboards, stat tiles and compact metrics — use the dedicated Radial Progress component. It is the canonical radial progress in WireKit: size and intent options, threshold coloring that turns the ring warning/danger past a limit, and a required accessible name.

progress.circle is deprecated — use radial-progress Do not use progress.circle in new code: it duplicates radial-progress under a second name and is slated for removal in v3.0.0 — which has no fixed date, the same wording prop naming conventions uses for every other change waiting on that version. It keeps working for the whole of v2; what is settled is that it will not gain anything, not when it goes. Migrate <x-wirekit::progress.circle :value="75" /> to <x-wirekit::radial-progress :value="75" label="…">75%</x-wirekit::radial-progress>. For an indeterminate ring, reach for the spinner — a radial progress is determinate by definition.

Livewire Integration

Pass a reactive value — the bar animates smoothly on updates:

<x-wirekit::progress :value="$this->uploadPercent" label="Uploading file" show-value />
public int $uploadPercent = 0;

public function processChunk()
{
    $this->uploadPercent = min(100, $this->uploadPercent + 10);
}

Width & Layout

The progress bar fills its parent width. Control the width with Tailwind classes directly on the component:

<x-wirekit::progress class="max-w-sm" :value="65" label="Upload" />

Height is controlled by the size prop (sm, md, lg).

Intent

intent is the canonical color axis (matching badge, button, and alert). variant keeps working as a back-compat alias; when both are present, intent wins.

Progress intents
Storage 80 / 100
Quota 55 / 100
Draft 20 / 100

Animated fill

animation adds optional motion to a determinate fill — the "work in flight" affordance for uploads and streaming. stripes is the barber-pole; shimmer is a single light sweep. Both are purely additive: the bar's value is unchanged, and the motion is disabled under prefers-reduced-motion (stripes freeze but stay visible; the shimmer sweep is removed).

Animated stripes while uploading
Uploading archive.zip 42 / 100
Shimmer sweep while uploading
Downloading update 68 / 100

Reduced Motion

WireKit disables all progress and skeleton animations when the user's OS has prefers-reduced-motion: reduce set. This is handled centrally in dist/wirekit.css — no extra configuration needed.

Client-side values

value is a PHP prop, evaluated when the page renders. An upload percentage does not exist then — it arrives later, in Alpine state that Livewire events advance. Binding to the rendered element does not close that: x-bind:value sets a value attribute on a <div>, and nothing reads it, so the bar stays indeterminate, show-value prints nothing, and there is no aria-valuenow at all.

value-expression takes the name of a property in the surrounding Alpine scope and lets the component drive the bar, the readout and aria-valuenow from it:

A bar driven from Alpine state
Uploading

Pass a property name, not an expression: value-expression="percent" rather than value-expression="percent * 2". The component resolves it through Alpine's scope chain, so it reads the state you already have without you wiring anything to the inner elements.

Why a name and not an expression. Under Alpine's CSP build there is no expression evaluator, so a computed binding goes inert — it is not evaluated, and nothing reports that. Passing a name keeps every calculation inside WireKit's own registered component, which the CSP build does allow — so value-expression works identically on both bundles.

No value yet is not zero. While the property is null, absent, empty or unparseable, the bar stays indeterminate and aria-valuenow is absent — a bar drawn at 0% would claim that no work has been done, which is a different statement from not knowing yet. An explicit 0 is a real value and renders as one.

Props

Prop Type Default Description
value numeric|null null Current value (null = indeterminate)
max numeric 100 Maximum value the bar represents
label string|null null Visible text label above the bar
showValue bool false Show current / max next to label
intent string 'primary' The color axis: 'primary', 'accent' (alias of primary), 'success', 'warning', 'danger', 'info', 'neutral'. See Variants & Intents.
variant string null Back-compat alias for intent. New code should use intent.
size string 'md' 'sm', 'md', 'lg'
valueExpression string|null null The NAME of an Alpine property to read the value from, for a value that only exists in the browser. See Client-side values.
animation string 'none' Optional fill motion (determinate only): 'none', 'stripes', 'shimmer'. Disabled under prefers-reduced-motion.
scope string|null null Scoped personalization name

Accessibility

  • role="progressbar" on the track — the WAI-ARIA progressbar role
  • Determinate: sets aria-valuenow, aria-valuemin="0", aria-valuemax="{max}" — screen readers announce the current percentage
  • Indeterminate: aria-valuenow is omitted (WAI-ARIA spec for unknown progress)
  • aria-labelledby links the bar to its visible label when label is set, so the label is part of the accessible name
  • Pass an id when the bar lives in a polling region. The link between label and bar needs an id, and with no id of your own one is generated. That is fine for a bar rendered once — but a wire:poll region re-renders, and a generated id changes with it, so the accessible name is re-resolved on every poll. An id you supply stays put across renders.
  • The indeterminate animation respects prefers-reduced-motion: users with reduced-motion settings see a static filled region instead of a sliding bar

Keyboard Interaction

This component is purely presentational and does not respond to keyboard input.

Pitfalls

  • Don't omit aria-label if the progress is not visually labeled. A bare <x-wirekit::progress> carries role="progressbar" but screen readers need either a sibling <x-wirekit::label> or an explicit aria-label.

Design Tokens

Element Token
Track background --color-wk-bg-muted
Track radius --radius-wk-full
Fill (accent) --color-wk-accent
Fill (success) --color-wk-success
Fill (warning) --color-wk-warning
Fill (danger) --color-wk-danger
Label color --color-wk-text
Value color --color-wk-text-muted
Transition --transition-wk-duration

Customization

Override defaults without publishing views via config/wirekit.php:

'components' => [
    'progress' => [
        'variant' => 'success',
        'size' => 'sm',
    ],
],

Usage & Conventions

Prop conventions — this component uses one or more of the shared semantic prop names (intent / variant / tone / surface). See Prop naming conventions for the canonical vocabulary, alias matrix, and decision tree.

Further Reading

Updated in WireKit v2.49.0 (2026-09-11)

Was this page helpful?

Thank you for your feedback!

Voting requires cookies or local storage. What we store