Source Code

<div class="container py-5 d-flex justify-content-center">
  <div class="card bsfp-card">
    <div class="card-body p-4">
      <div class="d-flex justify-content-between align-items-center mb-1">
        <h6 class="fw-bold mb-0">Complete your profile</h6>
        <span class="small fw-semibold text-muted" id="bsfpPercent">0%</span>
      </div>
      <div class="progress mb-3" style="height:6px;">
        <div class="progress-bar" id="bsfpBar" role="progressbar" style="width:0%"></div>
      </div>

      <div class="mb-2">
        <label class="form-label small fw-semibold mb-1">Full name</label>
        <input type="text" class="form-control form-control-sm bsfp-field" id="bsfpName">
      </div>
      <div class="mb-2">
        <label class="form-label small fw-semibold mb-1">Job title</label>
        <input type="text" class="form-control form-control-sm bsfp-field" id="bsfpTitle">
      </div>
      <div class="mb-2">
        <label class="form-label small fw-semibold mb-1">Company</label>
        <input type="text" class="form-control form-control-sm bsfp-field" id="bsfpCompany">
      </div>
      <div class="mb-2">
        <label class="form-label small fw-semibold mb-1">Short bio</label>
        <textarea class="form-control form-control-sm bsfp-field" id="bsfpBio" rows="2"></textarea>
      </div>
      <div class="mb-3">
        <label class="form-label small fw-semibold mb-1">Profile photo</label>
        <input type="file" class="form-control form-control-sm bsfp-field" id="bsfpPhoto">
      </div>

      <button type="button" class="btn btn-dark btn-sm fw-bold w-100" id="bsfpSubmit" disabled>Save profile</button>
    </div>
  </div>
</div>

Bootstrap Form Progress Indicator — Free HTML CSS JS Snippet

Bootstrap Form Progress Indicator · Forms · Plain HTML, CSS & JS · Live preview

What's included

Features

A single progress bar reflecting how much of one page is filled, distinct from a multi-step wizard
Correctly checks a file input's .files.length rather than its unreliable .value property
Text/textarea fields listen for "input"; the file field listens for "change" — the correct event per type
The bar visually turns green only once every field genuinely qualifies as filled
The submit button's disabled state is derived from the exact same completion count driving the bar

About this UI Snippet

Bootstrap Form Progress Indicator — HTML, CSS & JavaScript

Screenshot of the Bootstrap Form Progress Indicator snippet rendered live

This is a genuinely different pattern from a multi-step wizard like bootstrap-stepper-wizard-form — every field here lives on one single page at once, and the progress bar exists purely to communicate how much of that one page is filled in, not which of several discrete steps the user is currently on.

isFilled() is the one place that has to correctly handle two genuinely different field types: a text/textarea field counts as filled when its trimmed value is non-empty, while a file input has no meaningful .value to check at all — it's filled when .files.length > 0. Getting this branch wrong (checking .value on a file input, which browsers deliberately keep unreliable for security reasons) is a common bug in a homemade version of this pattern.

Every field type also needs the right event to actually notice a change — text inputs and textareas fire input on every keystroke, but a file input only ever fires change, never input, when a file is selected. The listener setup branches on that explicitly rather than attaching the same event name to every field and hoping it works, which is exactly the kind of subtle cross-field-type bug that's easy to miss until a coworker actually tries filling in the file field and the bar doesn't move.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Hand this snippet to an AI coding assistant like Claude and ask it to weight fields differently (e.g. required fields worth more than optional ones) instead of treating all fields equally, or to add a small checklist breakdown next to the bar showing exactly which fields are still missing.

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 Bootstrap 5.3 single-page form with a live completion-percentage progress bar, using the real Bootstrap CDN framework (bootstrap.min.css and bootstrap.bundle.min.js), not custom CSS made to resemble it.

Requirements:
- A form with at least 5 fields of mixed types, including at least one text input, one textarea, and one file input.
- A progress bar and percentage label above the fields, recomputed live as the user fills in each field, based on how many of the total fields currently qualify as "filled".
- Correctly detect a filled file input using its files.length property (not its value), and listen for its "change" event rather than "input" since file inputs never fire "input" on selection.
- The progress bar should turn a distinct success color once it reaches 100%, and a submit button should stay disabled until then, both derived from the same completion count.
- Clearing any previously filled field must immediately reduce the percentage and re-disable the submit button if it drops below 100%.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Load the snippetThe bar sits at 0%, "Save profile" is disabled, and every field is empty.
  2. 2
    Type your nameThe bar jumps to 20% immediately as you type the first character.
  3. 3
    Fill in job title, company, and bioThe percentage climbs with each field, updating live on every keystroke.
  4. 4
    Select a profile photo fileThe bar reaches 100%, turns green, and "Save profile" becomes enabled.
  5. 5
    Clear one of the text fieldsThe percentage immediately drops back below 100%, and Save disables again.

Real-world uses

Common Use Cases

Profile completion and onboarding forms
A well-known pattern (LinkedIn-style "your profile is 80% complete") for encouraging a user to finish filling in optional details.
Settings pages with several optional fields
Gives a visible sense of progress on a form with no natural multi-step structure.
CART
Checkout or application forms with many required fields
A live percentage is often reassuring feedback on a long single-page form before submission is even attempted.

Got questions?

Frequently Asked Questions

A file input's .value is deliberately kept unreliable by browsers for security reasons and cannot be used to detect a selection — .files.length is the correct, reliable way to check whether a file has actually been chosen.

A file input never fires the "input" event on selection (unlike text fields, which fire it on every keystroke) — it only fires "change", so a listener attached with the wrong event name would silently never detect a file selection at all.

This demo weighs all five fields equally for simplicity; a real implementation might weight fields differently (e.g. required fields counting more than optional ones) depending on what "100% complete" should actually mean for that specific form.

Yes. Track each field's value (and the file input's selected-file state) in component state, and derive the percentage and submit-disabled flag from that state on every render — the isFilled per-field-type logic carries over directly.