webdevpuneet.com

Progress Bar — Free HTML CSS JS Snippet

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

CategoryLoaders

Copy & paste

Source Code

<div class="page">

  <div class="section">
    <div class="sec-head"><span class="sec-label">Determinate — 68%</span><span class="sec-pct">68%</span></div>
    <div class="bar-wrap">
      <div class="bar-fill" style="width:68%;background:#6366f1"></div>
    </div>
  </div>

  <div class="section">
    <div class="sec-head"><span class="sec-label">Striped animated</span><span class="sec-pct">45%</span></div>
    <div class="bar-wrap">
      <div class="bar-fill striped" style="width:45%;background:#6366f1"></div>
    </div>
  </div>

  <div class="section">
    <div class="sec-head"><span class="sec-label">Gradient</span><span class="sec-pct">82%</span></div>
    <div class="bar-wrap">
      <div class="bar-fill" style="width:82%;background:linear-gradient(90deg,#6366f1,#ec4899)"></div>
    </div>
  </div>

  <div class="section">
    <div class="sec-head"><span class="sec-label">Indeterminate</span></div>
    <div class="bar-wrap">
      <div class="bar-fill indeterminate" style="background:#6366f1"></div>
    </div>
  </div>

  <div class="section">
    <div class="sec-head"><span class="sec-label">Multi-step upload</span><span class="sec-pct" id="pct-text">0%</span></div>
    <div class="bar-wrap">
      <div class="bar-fill" id="sim-bar" style="width:0%;background:#6366f1;transition:width 0.4s ease"></div>
    </div>
    <div class="step-row">
      <span class="step" id="step-label">Ready to upload</span>
      <button class="step-btn" id="step-btn" onclick="nextStep()">Start upload</button>
    </div>
  </div>

</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

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.

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.
Related: Staggered Skeleton List Reveal
See the Staggered Skeleton List Reveal for a related loaders pattern worth pairing with this one.

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.