Collapse & Accordion

Bootstrap · Lesson 20 of 28 6 min read Updated

Long pages are easier to read when details stay hidden until the reader asks for them: a "Read more" section, a list of filters, frequently asked questions. Bootstrap's collapse plugin shows and hides any element with a smooth animation, and the accordion component uses it to build a stack of panels where opening one closes the others.

In this lesson you will toggle content with collapse, control panels from JavaScript, and build an FAQ accordion with all its accessibility attributes.

Basic collapse

A collapse needs a target element with the collapse class (which hides it) and a trigger that points at it:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Collapse</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-4" style="max-width: 620px">
    <h1 class="h4">Ganga Aarti boat tour, Varanasi</h1>
    <p>A one-hour evening boat ride along the ghats to watch the aarti from the river.</p>
    <button class="btn btn-outline-primary btn-sm" type="button" data-bs-toggle="collapse"
            data-bs-target="#tourDetails" aria-expanded="false" aria-controls="tourDetails">
      Show details
    </button>
    <div class="collapse mt-3" id="tourDetails">
      <div class="card card-body">
        Starts at 6 pm from Dashashwamedh Ghat. Price ₹450 per adult, ₹200 for children under 10.
        Life jackets provided.
      </div>
    </div>

    <hr class="my-4">

    <p class="mb-2">Control two panels with one button, or each on its own:</p>
    <div class="d-flex gap-2 mb-3">
      <button class="btn btn-secondary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target=".day-plan"
              aria-expanded="false" aria-controls="day1 day2">Toggle both days</button>
      <button class="btn btn-outline-secondary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#day1"
              aria-expanded="false" aria-controls="day1">Day 1</button>
      <button class="btn btn-outline-secondary btn-sm" type="button" data-bs-toggle="collapse" data-bs-target="#day2"
              aria-expanded="false" aria-controls="day2">Day 2</button>
    </div>
    <div class="row g-3">
      <div class="col-sm-6"><div class="collapse day-plan" id="day1"><div class="card card-body">Day 1: Kashi Vishwanath, ghats walk, evening aarti.</div></div></div>
      <div class="col-sm-6"><div class="collapse day-plan" id="day2"><div class="card card-body">Day 2: Sarnath, silk weavers' market, return.</div></div></div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

How collapse works:

  • collapse hides the element. collapse show makes it visible from the start.
  • The trigger has data-bs-toggle="collapse" and data-bs-target, which is a CSS selector. #tourDetails targets one element; .day-plan targets every element with that class, so one button can toggle several panels.
  • While animating, Bootstrap swaps collapse for collapsing, which animates the height. Avoid padding or margin on the .collapse element itself, because it makes the animation jump. Put the padding on an inner element, as the examples do with card card-body.
  • aria-expanded tells screen readers whether the content is open, and Bootstrap updates it. aria-controls names the element (or elements, separated by spaces) that the button controls.

A link can be a trigger too: <a href="#tourDetails" data-bs-toggle="collapse" role="button">. A button is still the better choice, because the action happens on this page.

For a sideways slide, add collapse-horizontal to the collapse element and give its child a fixed width. It animates the width instead of the height.

Controlling collapse from JavaScript

JavaScript
const panel = document.getElementById("tourDetails");
const details = bootstrap.Collapse.getOrCreateInstance(panel, { toggle: false });

details.show();
details.hide();
details.toggle();

panel.addEventListener("shown.bs.collapse", () => {
  console.log("Details are now visible");
});

Pass { toggle: false } when creating the instance. By default, creating a Collapse instance also toggles it once, which surprises many people. The events are show.bs.collapse, shown.bs.collapse, hide.bs.collapse and hidden.bs.collapse.

Accordion

An accordion is a stack of collapsible panels with headers. It is the standard way to show FAQs. Each panel is an accordion-item with a header button and a collapsing body:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>FAQ accordion</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-4" style="max-width: 680px">
    <h1 class="h4 mb-3">Admission FAQ: Kochi Public School</h1>
    <div class="accordion" id="faq">
      <div class="accordion-item">
        <h2 class="accordion-header">
          <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faq1"
                  aria-expanded="true" aria-controls="faq1">
            When do admissions open?
          </button>
        </h2>
        <div id="faq1" class="accordion-collapse collapse show" data-bs-parent="#faq">
          <div class="accordion-body">Forms are available from 1 December to 15 January at the school office and online.</div>
        </div>
      </div>
      <div class="accordion-item">
        <h2 class="accordion-header">
          <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq2"
                  aria-expanded="false" aria-controls="faq2">
            What documents are needed?
          </button>
        </h2>
        <div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faq">
          <div class="accordion-body">Birth certificate, address proof, two photos and the previous school's report card.</div>
        </div>
      </div>
      <div class="accordion-item">
        <h2 class="accordion-header">
          <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq3"
                  aria-expanded="false" aria-controls="faq3">
            Is there a school bus?
          </button>
        </h2>
        <div id="faq3" class="accordion-collapse collapse" data-bs-parent="#faq">
          <div class="accordion-body">Yes. Buses cover Edappally, Kakkanad and Vyttila. The fee depends on distance.</div>
        </div>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

The important attributes:

  • The open panel has collapse show, and its button has aria-expanded="true" without collapsed. Closed panels have collapse only, and their buttons have the collapsed class (which turns the arrow) and aria-expanded="false". Keep these matched in your HTML; Bootstrap keeps them in sync after the first click.
  • data-bs-parent="#faq" on each panel is what makes it an accordion: opening one panel closes the others inside the same parent.
  • The button sits inside a heading (<h2 class="accordion-header">), so screen-reader users can jump between questions with heading shortcuts. Pick the heading level that fits your page outline.

Two variations:

  • Always open: remove data-bs-parent from the panels, and several can be open at once.
  • Flush: add accordion-flush to the accordion to remove the outer borders and rounded corners, so it sits edge to edge inside a card or sidebar.

The native alternative: details and summary

HTML's own <details> and <summary> elements also show and hide content, with no JavaScript at all, and giving several <details> the same name attribute makes them behave like an accordion. You can style them with Bootstrap utilities:

HTML
<details class="border rounded p-3 mb-2" name="faq">
  <summary class="fw-semibold">Is there a uniform shop?</summary>
  <p class="mt-2 mb-0">Yes, inside the campus, open 9 am to 1 pm.</p>
</details>

Bootstrap 5.3's accordion uses its JavaScript plugin, but the Bootstrap 6 alpha rebuilds the accordion on <details> and <summary>. For a simple FAQ, the native elements are a good choice today; see Details & Dialog.

Common mistakes

  • Padding on the .collapse element. The animation jumps at the end. Put padding on a child element.
  • Forgetting collapsed on closed accordion buttons. The arrows point the wrong way until the first click.
  • data-bs-parent pointing at the wrong element. Panels no longer close each other. It must match the accordion's id.
  • new bootstrap.Collapse(el) without { toggle: false }. The panel toggles the moment the instance is created.

Key points

  • collapse hides an element; a trigger with data-bs-toggle="collapse" and data-bs-target shows and hides it.
  • data-bs-target is a selector, so a class can control several panels at once.
  • Keep aria-expanded and aria-controls on triggers; avoid padding on the collapsing element itself.
  • An accordion is accordion > accordion-item > accordion-header + accordion-collapse; data-bs-parent makes only one panel open at a time.
  • accordion-flush removes outer borders, and <details name> is a no-JavaScript alternative for simple cases.

Exercise

Build a "Fee structure" accordion for a Jaipur college with three panels: Tuition, Hostel and Transport, each with one sentence and an amount in rupees. Use the flush style and the always-open behaviour, and start with every panel closed. Above it, add a "Show all fees" button that toggles all three panels at once by targeting a class they share.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Fee structure</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-4" style="max-width: 620px">
    <button class="btn btn-primary btn-sm mb-3" type="button" data-bs-toggle="collapse" data-bs-target=".fee-panel"
            aria-expanded="false" aria-controls="tuition hostel transport">Show all fees</button>
    <div class="accordion accordion-flush border rounded" id="fees">
      <div class="accordion-item">
        <h2 class="accordion-header">
          <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#tuition" aria-expanded="false" aria-controls="tuition">Tuition</button>
        </h2>
        <div id="tuition" class="accordion-collapse collapse fee-panel">
          <div class="accordion-body">₹48,000 per year, payable in two instalments.</div>
        </div>
      </div>
      <div class="accordion-item">
        <h2 class="accordion-header">
          <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#hostel" aria-expanded="false" aria-controls="hostel">Hostel</button>
        </h2>
        <div id="hostel" class="accordion-collapse collapse fee-panel">
          <div class="accordion-body">₹6,500 per month including meals.</div>
        </div>
      </div>
      <div class="accordion-item">
        <h2 class="accordion-header">
          <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#transport" aria-expanded="false" aria-controls="transport">Transport</button>
        </h2>
        <div id="transport" class="accordion-collapse collapse fee-panel">
          <div class="accordion-body">₹1,200 per month for routes inside Jaipur city.</div>
        </div>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

All buttons start with collapsed and every panel with plain collapse, so everything begins closed. The panels share the class fee-panel, so the top button's data-bs-target=".fee-panel" toggles all three. The panels have no data-bs-parent, which is what makes the accordion always-open.

Quick Quiz

1. Which class makes a collapse element visible when the page loads?

2. What makes accordion panels close each other when one opens?

3. Why should you avoid padding directly on a .collapse element?

4. What does { toggle: false } do in bootstrap.Collapse.getOrCreateInstance(el, { toggle: false })?

Learn Bootstrap with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call