HTML BasicsLesson 23 of 36

The progress Tag

The progress tag draws a bar that fills as a task completes. Uploads, downloads, file processing, form wizards: whenever there is a known amount of work and an amount done, progress is the right element. You set value for the work done and max for the total.

The progress tag draws a bar that fills as a task completes. Uploads, downloads, file processing, form wizards: whenever there is a known amount of work and an amount done, progress is the right element. You set value for the work done and max for the total.

Leave the value attribute off and the bar switches to an indeterminate mode, an animated stripe that moves back and forth. That is the correct look when the task has no measurable end, like a video buffering or a page waiting on a slow network request.

Why it exists: loading bars were built from divs and CSS animations for years. Progress gives the browser a native bar that screen readers understand, and JavaScript can update it live with a single property assignment. No drawing code, no width math.

Updating it live

Progress shines when a script updates it. You move the bar by setting its value property, and the browser repaints. A common mistake is rebuilding the element with innerHTML on every update; assigning the value property is cheaper and keeps any styling intact.

HTML
const bar = document.getElementById("load");
bar.value = 40;

Indeterminate mode

Omit the value attribute and the bar animates back and forth. This is the right look when the task has no measurable end. Screen readers get a busy indicator instead of a wrong percentage. Once the task becomes measurable, set value and max and the bar settles into determinate mode.

HTML
<progress>Loading...</progress>

Syntax

HTML
<progress value="70" max="100">70 percent</progress>

Omit value for Spinner, But Label It or Screen Readers Hear '35%' With No Context

Two traps: 1) To get an indeterminate spinner you must REMOVE the value attribute completely (element.removeAttribute('value')), setting value=0 makes a 0% bar, not spinner. 2) A bare <progress value="35"> is announced as '35 percent' with no idea what is loading - MDN and W3docs stress you must use <label> or aria-label. Text between tags is only fallback for ancient browsers, not accessible name.

HTML
<label for="dl">Downloading:</label><progress id="dl" value="35" max="100">35%</progress>
// indeterminate: <progress aria-label="Loading"></progress>

Good to know

  • value and max control the fill amount.
  • No value attribute means indeterminate.
  • Update bar.value from JavaScript, never innerHTML.
  • Pair it with fetch events for real uploads.
Try it Yourself Edit the code, press Run, then check what changed in the Result.
Ready
Code
Result

Practice makes it stick

Open the Playground and build your own page with the tags you just learned.

→ Open Playground