Progress Bar — Free HTML CSS JS Snippet

Progress Bar · Loaders · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Determinate: fixed width percentage on .bar-fill, transition: width for smooth fill
Striped: repeating-linear-gradient(-45deg) + background-position keyframe animation
Gradient fill: linear-gradient(90deg) on fill element
Indeterminate: translateX slide from -100% to 250% loop for unknown-duration ops
Multi-step upload: automated step progression with labels, timer, and button reset
All animations: background-position and transform — GPU compositor safe
Pill shape: border-radius: 999px on track and fill — no overflow:hidden needed

About this UI Snippet

Progress Bar — Determinate, Striped, Gradient, Indeterminate & Multi-Step Upload

Screenshot of the Progress Bar snippet rendered live

Progress bars communicate how far through a process the user is — whether uploading a file, completing a form, installing software, or finishing a course. The right progress bar variant depends on whether the completion percentage is known (determinate) or unknown (indeterminate). This snippet provides five variants covering every common progress bar use case.

Determinate is the base variant: a fixed-width .bar-fill inside a .bar-wrap track. Setting width: 68% on the fill represents 68% completion. The track uses background: #e2e8f0 (light grey) and border-radius: 999px for a pill shape. The fill inherits the border-radius. Animating width from 0 to a target value via CSS transition creates the fill-in effect.

Striped animated adds a repeating-linear-gradient of diagonal white stripes at -45deg over the fill background. background-size: 24px 24px controls stripe width. A @keyframes animation shifts background-position by 24px per cycle (one stripe width), creating the flowing stripe animation. The stripe movement runs on the compositor via background-position — no layout triggers.

Gradient uses background: linear-gradient(90deg, #6366f1, #ec4899) on the fill element. The gradient stretches across the full fill width regardless of the percentage, creating a colour shift from left to right. Combine with the striped animation for a striped gradient bar.

Indeterminate is for operations where completion percentage is unknown — API calls, file processing, database queries. The fill is fixed at 40% width and uses transform: translateX to slide from -100% (off-screen left) to 250% (off-screen right) in a loop. This communicates "working" without implying a known endpoint.

Multi-step upload simulation shows a realistic file upload flow: the user clicks "Start upload", the bar advances through 0→25→50→80→100% with step labels ("Connecting…", "Uploading file…", "Processing…") automatically advancing on a timer between steps. This teaches the pattern for wiring a real progress bar to upload events — XHR upload.onprogress gives a loaded/total ratio; fetch with ReadableStream gives chunk-by-chunk progress.

All fill animations use width transitions or CSS transforms — both GPU-safe properties that avoid main-thread layout recalculation.

Choosing the right variant for your use case

Use determinate when you have a measurable completion value — file upload bytes, form step count, course completion percentage. Use striped animated when the operation has a known endpoint but the precise percentage is unavailable — background task queues, bulk email sending, batch processing. Use indeterminate for truly unknown duration operations — API calls, database queries, server-side rendering. The gradient variant works identically to determinate but adds visual richness for high-visibility metric displays like profile completion or password strength.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't need to work out every keyframe by hand. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the striped animation shifts background-position instead of animating the stripe gradient's angle, or why the indeterminate bar's translateX runs from -100% to 250% rather than a simpler 0% to 100%. The same assistant can help optimize it, for instance checking whether the multi-step simulation's chained setTimeout calls in nextStep should be replaced with a single timeline so the delays stay easy to tune, or whether the striped background-size could be reduced on low-power devices. It's equally useful for extending the bars: ask it to wire the determinate variant to a real XHR upload.onprogress handler, add a pause/cancel control to the multi-step simulation, or add a buffered-progress second bar like a video scrubber shows. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a set of progress bar variants in plain HTML, CSS, and JavaScript with no libraries.

Requirements:
- A determinate bar: a pill-shaped track div containing a fill div whose width is set to a percentage inline style, with a CSS transition on width so JavaScript-driven percentage changes animate smoothly instead of snapping.
- A striped animated bar: the fill uses a repeating-linear-gradient of semi-transparent diagonal stripes at -45 degrees with a fixed background-size, and a keyframe animation that shifts background-position by exactly one stripe-width per cycle so the stripes appear to flow continuously — the animation must only touch background-position, never width or transform, so it stays compositor-friendly.
- A gradient-filled bar: the fill uses a linear-gradient background spanning two colors, stretching across whatever the current fill width is.
- An indeterminate bar for unknown-duration operations: a fill fixed at a partial width (e.g. 40%) that uses a keyframe animation translating it from off-screen left to off-screen right in a continuous loop, with no explicit percentage or ARIA valuenow implied, so it clearly reads as "something is happening" rather than a measurable quantity.
- A multi-step upload simulation: a JavaScript array of steps, each with a target percentage and a status label (e.g. "Connecting...", "Uploading file...", "Processing...", "Upload complete"), a button that starts the sequence, and a function that advances through the array — updating the bar's width, a percentage readout, and the status label — automatically chaining to the next step after a short delay until it reaches the final step, then re-enabling the button to restart.

Step by step

How to Use

  1. 1
    Click "Start upload" to see the multi-step simulationThe bar advances from 0% through 25%, 50%, 80%, to 100% with step labels updating automatically. The button re-enables at completion to reset.
  2. 2
    Set the percentage on a determinate barUpdate style="width: X%" on the .bar-fill element directly in HTML. For JS-driven updates: document.querySelector(".bar-fill").style.width = percentage + "%" — add transition: width 0.4s ease to CSS for smooth animation.
  3. 3
    Switch between variantsCopy only the CSS class you need. Remove the striped class for a plain bar, add it for stripes, add indeterminate for unknown-duration operations. Mix gradient fill background with the striped animation on the same element.
  4. 4
    Wire to a real file uploadUse XMLHttpRequest with xhr.upload.addEventListener("progress", e => { const pct = (e.loaded/e.total)*100; bar.style.width = pct+"%"; }). For fetch + ReadableStream, read chunks and accumulate loaded bytes against Content-Length header.
  5. 5
    Change height and colourUpdate height on .bar-wrap (default 10px) and the fill background colour. For a thinner progress line, use 3-4px. For a thick progress bar, use 16-20px. Adjust border-radius accordingly.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component with useState for the progress value, or "Tailwind" for a React + Tailwind CSS version.

Real-world uses

Common Use Cases

File upload progress with XHR or fetch ReadableStream
The multi-step simulation shows the exact pattern for a real file upload: connect, upload, process, complete. Wire each phase to xhr.upload.onprogress for bytes-based progress. The determinate bar fills smoothly with a CSS width transition.
Course completion, onboarding, and profile strength indicators
The determinate bar communicates how far a user has progressed through an onboarding flow, course curriculum, or profile completion checklist. Show it in the header or sidebar with a percentage label to motivate completion.
API call and background job loading states
The indeterminate slide variant is ideal when the completion time is unknown — API calls, image processing, database migrations, and background jobs. It communicates "working" without creating false expectations about timing. For content placeholders instead, reach for a skeleton loader or loading dots.
Software installation and update progress screens
Multi-phase install flows (downloading, extracting, installing, configuring) map directly to the multi-step simulation. Each phase has a label and a percentage range. Advance the bar programmatically as each phase completes.
Learn the striped bar animation CSS technique
The striped animation uses background-position keyframe animation on a repeating diagonal gradient — a background-position change runs on the GPU compositor without layout recalculation. The same technique applies to any striped loading bar in any project.
Skill level and strength meter displays
A filled determinate bar labelled with a skill name and percentage communicates skill level or password/content strength. The gradient variant (indigo to pink) adds visual hierarchy for a "strength" reading — dark for weak, bright for strong.

Got questions?

Frequently Asked Questions

Use XMLHttpRequest for progress events: const xhr = new XMLHttpRequest(); xhr.upload.addEventListener("progress", e => { if (e.lengthComputable) { const pct = Math.round((e.loaded / e.total) * 100); bar.style.width = pct + "%"; pctText.textContent = pct + "%"; } }); xhr.open("POST", "/upload"); xhr.send(formData). The XHR upload progress event fires repeatedly during transmission, giving real bytes-based progress. The Fetch API does not natively support upload progress — use XHR or a library like axios for uploads.

A determinate bar shows a specific completion percentage — it implies the system knows how much work remains. Use it when you have a measurable quantity: bytes uploaded out of total bytes, steps completed out of total steps, items processed out of total items. An indeterminate bar shows only that "something is happening" — no percentage. Use it for operations where you cannot calculate progress: a network API call with unknown response time, a database query, an image processing task.

Add transition: width 0.4s ease to the .bar-fill CSS rule. Then setting bar.style.width = pct + "%" from JavaScript triggers the CSS transition automatically. The browser interpolates from the current width to the new width over 0.4s. For very rapid updates (real upload progress), reduce the transition duration to 0.1–0.2s so it does not lag behind the actual upload speed.

Click "JSX" to download. Manage const [progress, setProgress] = useState(0) in state. Set the fill width via style={{ width: progress + "%" }}. For a file upload, create an XHR in a useEffect or event handler and call setProgress in the upload.onprogress callback. For a multi-step flow, advance progress state in each step completion handler.