---
title: "JavaScript Progress"
description: "The LemonadeJS Progress block for JavaScript: Linear and circular progress indicators. Contract-verified, framework-agnostic, zero dependencies, with a live example."
source: https://lemonadejs.com/docs/plugins/progress/
---

<link rel="stylesheet" href="/v6/progress.css">

# JavaScript Progress

`@lemonadejs/progress` · ✓ 17 contract checks · framework-agnostic · zero dependencies

One block, both progress shapes: linear (track + bar)
and circular (SVG stroke arc), selected by `type`. Determinate
when a percent is bound, indeterminate otherwise (or when forced):

  <${Progress} bind="${pct}" label />                 linear, determinate
  <${Progress} type="circular" bind="${pct}" />       circular, determinate
  <${Progress} />                                     linear, indeterminate
  <${Progress} type="circular" indeterminate />       spinner

bind vs indeterminate (by design):
  bind="${state}"  the live percent 0-100 (clamped); ABSENT → indeterminate
  indeterminate    forces the looping animation even with a value

Geometry is deterministic and testable: the linear bar carries an inline
width:%, the circular arc carries stroke-dasharray/stroke-dashoffset
computed from percent and the radius (size - thickness) / 2. The looping
animations are pure CSS keyframes driven by data-indeterminate.

## Example

<!--example-->

```js
import { html } from 'lemonadejs';
import Progress from '@lemonadejs/progress';

const App = (props, { state, onUnmount }) => {
    const percent = state(0);
    // A fake upload: one bound percent drives every determinate bar below
    const timer = setInterval(() => (percent.value = percent.value >= 100 ? 0 : percent.value + 1), 60);
    onUnmount(() => clearInterval(timer));

    return html`<div style="display:grid;gap:18px;max-width:420px">
        <${Progress} bind="${percent}" label />
        <${Progress} bind="${percent}" thickness="8" color="green" />
        <div style="display:flex;gap:24px;align-items:center">
            <${Progress} type="circular" bind="${percent}" label size="64" thickness="5" color="orange" />
            <${Progress} type="circular" indeterminate />
        </div>
        <${Progress} indeterminate color="purple" />
    </div>`;
};
```

## Installation

```bash
npm install @lemonadejs/progress
```

```js
import Progress from '@lemonadejs/progress';
import '@lemonadejs/progress/style.css';
```

Three deployment forms, one component:

```js
html`<${Progress} />`                       // by value (no registration)
setComponents({ Progress });               // then <Progress /> by name anywhere
createWebComponent(Progress);              // <lm-progress> in plain HTML/any framework
```

## Props

Every declared prop arrives as a **live state** — pass a value for a snapshot or a
state for a two-way live wire. Attribute strings are coerced to the declared type.

| Prop | Type | Default | Description |
|---|---|---|---|
| `bind` | number | — | Two-way bound value. `.set()` fires `onchange`; plain assignment is silent. two-way percent 0-100; absent → indeterminate |
| `type` | string | `''` | '' = linear | 'circular' (data-type variant) |
| `indeterminate` | boolean | `false` | force the looping animation even with a value |
| `size` | number | `0` | circular diameter in px (default 40 via CSS) |
| `thickness` | number | `0` | stroke/bar thickness in px (defaults: 4 linear, 3.6 circular) |
| `color` | string | `''` | green | orange | red | purple (default blue) |
| `label` | boolean | `false` | show the % text: beside linear, centered in circular |
| `arialabel` | string | `''` | accessible name for the progressbar (aria-label) |

## Events

All event names are lowercase (the platform convention — LJS-305 warns otherwise).

- `onchange` — fires when the bound percent is set via set()

## Styling

All classes follow the `lm-progress-*` convention; visual variants are `data-*`
attributes on the root. Override freely — there is no styling engine to fight.

## Contract

The machine-readable schema ships with the package:

```js
import contract from '@lemonadejs/progress/contract.json';
```

`verify.json` carries the conformance proof produced by `verify(Progress)`.

Looking for the v5 plugin? See the [archived v5 documentation](/docs/v5/plugins/).