Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

The <progress> Element

A comprehensive beginner-to-guru lesson on the HTML <progress> element, determinate and indeterminate task progress, value and max, accessibility, DOM APIs, styling boundaries, asynchronous workflows, testing, and resilient production patterns.

Levels: Beginner, Intermediate, Advanced ~50 min read

Basic

What <progress> represents

The <progress> element represents completion progress for a task. It is about work moving toward completion, not a general-purpose measurement of a quantity.

Quick check

Complete the markup with the element for indicating completion of a task.

< value="70" max="100">70%</progress>

Your first progress bar

Start with a real task: 70 of 100 units are complete. The important idea is that the value represents work completed toward a known maximum.

html
<label for="download">Download progress</label>
<progress id="download" value="70" max="100">70%</progress>
Interactive Runnable example
Edit

Ready to run.

Determinate progress

A determinate progress bar has a value. The value represents completed work and is interpreted against max. If max is omitted, the effective maximum is 1.

Indeterminate progress

When the value attribute is absent, the progress is indeterminate. This means work is ongoing but the page does not know how much remains or cannot provide a meaningful fraction.

Basic syntax

A determinate progress indicator needs a value. Add max when the task has a total greater than 1, and keep the fallback text useful for older user agents.

html
<label for="upload">Upload progress</label>
<progress id="upload" value="3" max="10">3 of 10</progress>
Interactive Runnable example
Edit

Ready to run.

The max attribute

max defines how much total work the task requires. If omitted, the effective maximum is 1. A valid max must be greater than zero.

The value attribute

value specifies completed work. With max present it is constrained to the range from 0 through max. Without max, it is in the range from 0 through 1.

The minimum is always zero

Unlike <meter>, <progress> has no min attribute. Its progress range always begins at zero.

Fallback text

Text inside <progress> can provide a useful fallback for older user agents. Modern user agents expose the progress semantics separately, so do not treat the fallback text as the authoritative numeric value.

<progress> is not <meter>

Use <progress> when the value answers “how much of this task is complete?” Use <meter> when the value is a scalar or fractional measurement within a known range, such as storage usage or a score.

Intermediate

Labeling progress

A progress indicator normally needs an accessible label. A <label> can label a progress element, and ARIA labeling mechanisms can also be used where appropriate.

Label is different from fallback content

The human-readable text between the progress tags is not the accessible name merely because it is visible. Use an actual label relationship or an appropriate accessible naming mechanism.

Progress for a busy region

When progress describes loading or updating a larger region, the region can expose its busy state and reference the progress indicator with aria-describedby. Remove the busy state when the operation finishes.

Updating progress with JavaScript

Update the element.value property as work advances. Keep any visible percentage or status text synchronized when users need a textual representation too.

Interactive Runnable example
Edit

Ready to run.

Switching back to indeterminate

To make an existing determinate progress bar indeterminate, remove the value attribute. Setting a magic numeric value does not represent indeterminate state.

Zero progress is determinate

value="0" is a real determinate state: zero work has completed. It is different from omitting value, which means the amount of remaining work is not represented.

Units are application-defined

The specification does not assign physical units to progress values. A value of 7 out of 10 could mean files, steps, records, bytes, or another unit chosen by the application.

The position property

HTMLProgressElement.position exposes the current fraction as a value from 0 to 1 for determinate progress and -1 for indeterminate progress.

The labels property

HTMLProgressElement.labels returns the label elements associated with the progress element. This lets scripts inspect the semantic labeling relationship rather than searching arbitrary text.

The max property

HTMLProgressElement.max reflects the effective maximum and defaults to 1. Invalid or non-positive max content does not create a usable positive maximum.

The value property

HTMLProgressElement.value exposes the current progress value. Setting it through the DOM API lets application code update progress without rewriting the surrounding markup.

Content model

Progress accepts phrasing content, but a progress element cannot contain another progress element. This makes nested progress indicators semantically invalid.

HTML categories

Progress is flow content, phrasing content, labelable content, and palpable content. Its labelable status matters when designing accessible form-adjacent interfaces.

Do not replace native semantics with role

The native progress semantics already map to a progressbar concept. The HTML standard does not permit arbitrary role substitution on <progress>; use the native element instead of recreating it with a generic element plus role="progressbar" when native semantics fit.

The progressbar concept

Assistive technologies can expose native progress semantics as a progressbar. The HTML element supplies the platform semantics, while the application supplies a meaningful accessible label and current value.

Styling the native widget

Browsers render <progress> using native widget conventions. CSS can style it, but detailed internal parts and pseudo-elements are browser-specific, so production styling should tolerate implementation differences.

Styling indeterminate state

The :indeterminate pseudo-class can distinguish an indeterminate progress element. Do not depend on a single vendor-specific internal pseudo-element for the meaning of progress.

Advanced

Progress is not a generic status message

A progress bar communicates task completion. It does not replace a status message such as “Upload failed” or “Connected.” Provide separate textual status when the user needs outcome or error information.

Progress is not a spinner

A spinner communicates ongoing activity without quantifying completion. Use indeterminate <progress> when the semantics are specifically task progress and a generic activity indicator when there is no meaningful progress model.

Why <meter> is different

A storage-capacity gauge such as “37 GB used of 100 GB” is a measurement of a quantity, not necessarily completion of a task. <meter> is designed for that meaning.

Why <output> is different

<output> represents a calculation result or user-action result. It does not represent task completion. A workflow can legitimately use both: progress communicates completion while output communicates a calculated result.

Relationship to forms

Progress is labelable but is not a normal user-editable form control. Do not assume that placing it inside a form makes its value a successful submitted form field.

Submission boundary

If a server needs the progress value, send it explicitly through an actual form control or application request. Do not rely on <progress> as a submitted field.

Constraint-validation boundary

<progress> is not an input mechanism for users and is not a substitute for validating user data. Keep task-state reporting separate from constraint-validation UI.

Events and application state

Changing progress is normally a consequence of application state changes. There is no requirement to wait for a special progress-element event; update the element when the underlying task state changes.

Asynchronous work

For asynchronous tasks, establish a monotonic or otherwise well-defined progress model before updating the UI. Network retries, unknown totals, parallel work, and cancellation can make naive percentages misleading.

Unknown totals

If the application cannot determine total work, use an indeterminate progress bar instead of inventing an arbitrary maximum that creates false precision.

Parallel tasks

When multiple operations contribute to one overall task, define the aggregation rule explicitly. A simple average of percentages is often wrong when operations have different workloads.

Weighted progress

If stages have different costs, calculate overall progress from actual work units or declared weights. The HTML element does not know what your application units mean.

Avoid false precision

Showing 99.8% can imply knowledge the system does not possess. Match displayed precision to the reliability of the underlying estimate.

Completing a task

When work reaches its defined maximum, set value to max and separately communicate completion if users need a confirmation, result, or next action.

Failure handling

A failed operation is not represented by a special <progress> value. Preserve the progress state if useful, then expose failure through appropriate status or error content.

Cancellation

Cancellation is an application state, not an HTML progress state. Stop updates and communicate that the operation was cancelled rather than leaving a moving indicator running.

Security boundary

Never treat a progress percentage as proof that a server-side operation completed. The UI is a client-side representation and can be modified by the user.

Untrusted progress data

If progress is derived from server responses, validate and constrain the data before using it. Do not insert server-provided strings into HTML merely to display a percentage.

Localization

Percentages, units, and surrounding status messages may require localization. The semantic numeric value is application data; the visible wording should follow the page language and locale conventions.

RTL interfaces

The meaning of progress is direction-independent, but the visual direction of a native progress widget can vary with writing mode and platform conventions. Test RTL rather than assuming a left-to-right fill direction.

Testing semantics

Tests should verify the native <progress> element, value/max state, determinate versus indeterminate behavior, accessible labeling, and important application transitions.

Testing the DOM API

Test value, max, position, and labels through the DOM interface. Do not infer the semantic state only from computed CSS or screenshots.

Accessibility testing

Check that the progress indicator has an accessible name, communicates the intended current state, and is associated with any busy region it describes.

Testing fallback

If fallback text is important for a compatibility target, verify the actual fallback content separately from the native progress semantics.

Browser variance

Native progress rendering differs between browsers and operating systems. Treat exact geometry, colors, internal pseudo-elements, and animation as presentation details rather than portable HTML semantics.

Performance-aware updates

Do not update progress on every tiny internal event if doing so creates unnecessary layout or rendering work. Throttle or coalesce updates while preserving useful feedback.

Do not overwhelm assistive technology

Rapidly changing progress can produce noisy announcements depending on platform behavior. Provide stable labeling and separate important status messages rather than forcing every numerical tick to be announced.

Separate task state from presentation

Keep the canonical task state in application logic. Treat <progress> as a semantic presentation of that state, making it easy to swap visual treatments without changing the underlying model.

Upload example architecture

An upload UI can use <progress> for bytes transferred, a textual status for connection or error state, and a final result message for completion. These are three different meanings and should not be collapsed into one element.

Installation example

An installer can expose stage completion with progress while separately showing the current stage name. The progress value should represent work completed, not merely elapsed time unless elapsed time is explicitly the chosen proxy.

Learning workflow example

A course can use progress for completed lessons when “lessons completed” is genuinely the task metric. If the value is only an estimate of mastery, use a different semantic model.

Guru mental model

Think of <progress> as a semantic projection of a task-completion model: zero-to-max work, or an explicitly indeterminate state. HTML provides the semantics and APIs; your application defines the units, aggregation, lifecycle, and truthfulness of the data.

Production checklist

Before shipping, verify that the task really has completion semantics, choose determinate or indeterminate correctly, provide an accessible label, keep status and errors separate, avoid false precision, handle cancellation and failure, test DOM state, and tolerate native rendering differences.

Scope boundaries

This lesson owns <progress>. <meter> owns scalar/range measurements, <output> owns calculation results, and cross-element form validation/submission lessons own those broader workflows. Do not duplicate their specifications here.

Guru practice

Build a resilient upload-progress component that supports known and unknown totals, retries, cancellation, failure, accessible labeling, localized status text, throttled updates, and a final success state. Your HTML should use native <progress> semantics rather than recreating a progressbar from generic elements.

Was this page helpful?

Mark this learning resource complete to track your learning progress.