✦ A decade of Canvas craft, now driven by AI — describe it, watch it build live.Start building
Bootstrap 5Navigation

Bootstrap 5 Step Wizard

A step wizard is a multi-step navigation pattern that guides users through a sequential process, displaying their current position and progress across a series of discrete stages. It is composed from Bootstrap 5 primitives including flexbox utilities, badges, the progress component, cards, and collapse or tab-pane visibility helpers. Use it for multi-step forms, onboarding sequences, checkout flows, or any workflow where tasks must be completed in order.

Primary Class

d-flex flex-column gap-3

Common Use Cases

  • A SaaS onboarding flow that walks new users through account setup, team invitation, billing details, and plan selection across four numbered steps.
  • An e-commerce checkout process divided into distinct stages — basket review, delivery address, payment method, and order confirmation — each validated before proceeding.
  • A loan or insurance application form split into personal details, financial information, document upload, and submission review to prevent overwhelming the applicant.
  • An admin-panel data-import tool that steps a user through file upload, column mapping, validation preview, and final import confirmation in sequence.

Variants & Classes

VariantDescription
Horizontal Step BarSteps rendered in a horizontal row with numbered badges connected by a progress line, suitable for four or fewer steps on desktop layouts.
Vertical Step ListSteps stacked vertically down the left side with a connecting border, ideal for mobile-first layouts or wizards with many steps and long labels.
Step Bar with ProgressCombines the horizontal step bar with Bootstrap's own progress component beneath it to give a numeric percentage alongside the visual step indicators.
Card-per-StepEach step's content lives inside a Bootstrap card; only the active card is shown using d-none and d-block toggled via JavaScript, keeping markup self-contained.

Code Example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Step Wizard</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
  <style>
    .step-wizard-bar { position: relative; }
    .step-wizard-bar::before {
      content: '';
      position: absolute;
      top: 20px;
      left: 10%;
      width: 80%;
      height: 2px;
      background-color: #dee2e6;
      z-index: 0;
    }
    .step-item { position: relative; z-index: 1; }
    .step-circle {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-weight: 600;
      font-size: 0.9rem;
      border: 2px solid #dee2e6;
      background: #fff;
      color: #6c757d;
      margin: 0 auto 0.4rem;
      transition: background-color 0.2s, border-color 0.2s, color 0.2s;
    }
    .step-item.completed .step-circle { background-color: #0d6efd; border-color: #0d6efd; color: #fff; }
    .step-item.active .step-circle   { background-color: #0d6efd; border-color: #0d6efd; color: #fff; box-shadow: 0 0 0 4px rgba(13,110,253,.25); }
    .step-item.completed .step-label,
    .step-item.active .step-label    { color: #0d6efd; font-weight: 600; }
    .step-label { font-size: 0.78rem; color: #6c757d; }
  </style>
</head>
<body class="bg-light py-5">
<div class="container" style="max-width:680px">

  <!-- Step indicator bar -->
  <div class="step-wizard-bar d-flex justify-content-between align-items-start mb-4" id="stepBar">
    <div class="step-item text-center completed" data-step="1">
      <div class="step-circle">&#10003;</div>
      <span class="step-label d-block">Account</span>
    </div>
    <div class="step-item text-center active" data-step="2">
      <div class="step-circle">2</div>
      <span class="step-label d-block">Profile</span>
    </div>
    <div class="step-item text-center" data-step="3">
      <div class="step-circle">3</div>
      <span class="step-label d-block">Billing</span>
    </div>
    <div class="step-item text-center" data-step="4">
      <div class="step-circle">4</div>
      <span class="step-label d-block">Confirm</span>
    </div>
  </div>

  <!-- Progress bar -->
  <div class="progress mb-4" style="height:6px">
    <div class="progress-bar" role="progressbar" style="width:33%" aria-valuenow="33" aria-valuemin="0" aria-valuemax="100"></div>
  </div>

  <!-- Step panels -->
  <div class="card shadow-sm border-0">
    <div class="card-body p-4">

      <!-- Step 1 -->
      <div class="step-panel d-none" id="panel1">
        <h5 class="card-title mb-1">Account Details</h5>
        <p class="text-muted small mb-3">Create your login credentials.</p>
        <div class="mb-3">
          <label class="form-label">Email address</label>
          <input type="email" class="form-control" placeholder="you@example.com">
        </div>
        <div class="mb-3">
          <label class="form-label">Password</label>
          <input type="password" class="form-control" placeholder="Min. 8 characters">
        </div>
      </div>

      <!-- Step 2 (active) -->
      <div class="step-panel d-block" id="panel2">
        <h5 class="card-title mb-1">Your Profile</h5>
        <p class="text-muted small mb-3">Tell us a bit about yourself.</p>
        <div class="row g-3">
          <div class="col-sm-6">
            <label class="form-label">First name</label>
            <input type="text" class="form-control">
          </div>
          <div class="col-sm-6">
            <label class="form-label">Last name</label>
            <input type="text" class="form-control">
          </div>
          <div class="col-12">
            <label class="form-label">Job title</label>
            <input type="text" class="form-control">
          </div>
        </div>
      </div>

      <!-- Step 3 -->
      <div class="step-panel d-none" id="panel3">
        <h5 class="card-title mb-1">Billing</h5>
        <p class="text-muted small mb-3">Choose a plan and enter payment details.</p>
        <div class="mb-3">
          <label class="form-label">Card number</label>
          <input type="text" class="form-control" placeholder="4242 4242 4242 4242">
        </div>
        <div class="row g-3">
          <div class="col-6">
            <label class="form-label">Expiry</label>
            <input type="text" class="form-control" placeholder="MM / YY">
          </div>
          <div class="col-6">
            <label class="form-label">CVC</label>
            <input type="text" class="form-control" placeholder="123">
          </div>
        </div>
      </div>

      <!-- Step 4 -->
      <div class="step-panel d-none" id="panel4">
        <h5 class="card-title mb-1">Confirm &amp; Submit</h5>
        <p class="text-muted small mb-3">Review your details before submitting.</p>
        <ul class="list-group list-group-flush mb-2">
          <li class="list-group-item px-0 d-flex justify-content-between"><span class="text-muted">Email</span><span>you@example.com</span></li>
          <li class="list-group-item px-0 d-flex justify-content-between"><span class="text-muted">Name</span><span>Jane Smith</span></li>
          <li class="list-group-item px-0 d-flex justify-content-between"><span class="text-muted">Plan</span><span>Pro &mdash; &pound;29/mo</span></li>
        </ul>
      </div>

    </div>

    <!-- Navigation -->
    <div class="card-footer bg-white d-flex justify-content-between border-top py-3">
      <button class="btn btn-outline-secondary" id="btnBack" onclick="changeStep(-1)" disabled>Back</button>
      <button class="btn btn-primary" id="btnNext" onclick="changeStep(1)">Next &rarr;</button>
    </div>
  </div>

</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
  const TOTAL = 4;
  let current = 2;

  function changeStep(dir) {
    const next = current + dir;
    if (next < 1 || next > TOTAL) return;
    // Hide current panel
    document.getElementById('panel' + current).classList.replace('d-block','d-none');
    // Update bar
    const oldItem = document.querySelector('[data-step="' + current + '"]');
    oldItem.classList.remove('active');
    if (dir > 0) {
      oldItem.classList.add('completed');
      oldItem.querySelector('.step-circle').innerHTML = '&#10003;';
    } else {
      oldItem.classList.remove('completed');
      oldItem.querySelector('.step-circle').textContent = current;
    }
    current = next;
    // Show new panel
    document.getElementById('panel' + current).classList.replace('d-none','d-block');
    // Mark active
    const newItem = document.querySelector('[data-step="' + current + '"]');
    newItem.classList.add('active');
    newItem.classList.remove('completed');
    if (newItem.querySelector('.step-circle').innerHTML === '&#10003;') {
      newItem.querySelector('.step-circle').textContent = current;
    }
    // Progress bar
    const pct = Math.round(((current - 1) / (TOTAL - 1)) * 100);
    const bar = document.querySelector('.progress-bar');
    bar.style.width = pct + '%';
    bar.setAttribute('aria-valuenow', pct);
    // Buttons
    document.getElementById('btnBack').disabled = current === 1;
    document.getElementById('btnNext').textContent = current === TOTAL ? 'Submit' : 'Next \u2192';
  }
</script>
</body>
</html>

Live Examples

Vertical Step List (mobile-friendly)

A vertically stacked step indicator using a left border and Bootstrap spacing utilities, well-suited to narrow viewports or sidebars.

Example 1

Breadcrumb-style Compact Step Bar

A lightweight inline version using Bootstrap badges and chevron separators, appropriate for wizards embedded within a larger page layout where vertical space is limited.

Example 2

Step Wizard with Inline Validation Feedback

Combines the step panel pattern with Bootstrap's native validation classes so that errors are shown before the user can advance, using was-validated on the form.

Example 3

Canvas Framework Variants

The Canvas template extends Bootstrap 5 with 1,658+ component variants. Generate any of these using Canvas Builder:

  • Horizontal four-step checkout wizard with progress bar, generated from a single prompt in Canvas Builder
  • Vertical onboarding step list with icon circles, auto-built by Canvas Builder for mobile-first layouts
  • Compact breadcrumb step bar embedded in a navbar, produced via Canvas Builder's navigation prompt
  • Card-per-step wizard with back and next navigation, scaffolded instantly by Canvas Builder
  • Step wizard with live validation feedback, generated by Canvas Builder from a multi-step form prompt

Best Practices

Use d-none / d-block rather than Bootstrap collapse for step panels

Bootstrap's collapse component adds vertical animation which is distracting in a wizard context where users expect an immediate panel swap. Toggle d-none and d-block directly in JavaScript to keep transitions snappy and predictable. If you want a subtle transition, add a fade class and control opacity with a short CSS transition instead.

Gate the Next button with client-side validation before advancing

Call the HTML5 Constraint Validation API (form.checkValidity()) on the active panel's inputs before calling changeStep(1). Set the form element's novalidate attribute and add the was-validated class yourself on failure — this lets Bootstrap's built-in validation styles render without a native browser submit. This prevents users advancing with empty required fields without a full page reload.

Ensure the step indicator is accessible with ARIA

Wrap the step bar in a <nav> with aria-label="Progress" and mark the active step's circle with aria-current="step". Completed steps should have aria-label values like "Step 1, Account, completed" so screen-reader users understand both position and state without relying on visual cues alone. Avoid using <div> elements as interactive controls — if a step is clickable, use a <button>.

Keep the progress-bar value in sync with the step index

Calculate the percentage as ((currentStep - 1) / (totalSteps - 1)) * 100 so the bar reads 0% on step one and 100% only when the final step is active. Always update aria-valuenow alongside style.width so assistive technologies announce accurate progress. Rounding to the nearest integer avoids sub-pixel rendering differences across browsers.

FAQ

Does Bootstrap 5 have an official step wizard component?
No. Bootstrap 5 ships no step wizard or stepper component. The pattern shown here is composed entirely from official Bootstrap 5.3 utilities and primitives — flexbox helpers, badges, the progress component, cards, and spacing utilities — combined with a small amount of custom CSS for the connector line and circle styles.
How do I make the step wizard work across multiple HTML pages instead of a single page?
Store the current step index in sessionStorage or as a query-string parameter (e.g. ?step=2). On each page load, read that value, apply the completed and active classes to the step bar, and show the correct panel. Submit each step's form data to your server (or accumulate it in sessionStorage) before redirecting to the next URL, then clear the stored state after the final submission.
Can I use Bootstrap's tab component to power the step panels?
You can, but it requires care. Bootstrap's tab component allows free navigation between tabs by default — you would need to intercept the shown.bs.tab event and programmatically block tab switches unless validation passes. It is often simpler to manage panel visibility yourself with d-none and d-block, as shown in the code examples, so that the navigation logic remains fully under your control.
How should I handle a step wizard on small mobile screens where the horizontal bar does not fit?
Use Bootstrap's responsive display utilities to show the horizontal bar only on sm breakpoints and above (d-none d-sm-flex) and replace it with the compact breadcrumb-style or vertical list variant at xs. Alternatively, show only the current step label and a "Step X of Y" counter on mobile — this avoids truncated text and overlapping circles on screens narrower than 360 px.