Buttons & Button Groups

Bootstrap · Lesson 9 of 28 6 min read Updated

Buttons are how users act on a page: pay, save, search, delete. Bootstrap's button classes give every button a consistent shape, colour, hover effect, focus ring and disabled state, so users always recognise what can be clicked.

In this lesson you will style buttons, change their size and state, make full-width buttons, and group related buttons together, including radio-style choices built from buttons.

Basic buttons

Every Bootstrap button needs two classes: btn for the shape and one variant for the colour.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Buttons</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-3">
    <h1 class="h5">Solid</h1>
    <div class="d-flex flex-wrap gap-2 mb-3">
      <button type="button" class="btn btn-primary">Primary</button>
      <button type="button" class="btn btn-secondary">Secondary</button>
      <button type="button" class="btn btn-success">Success</button>
      <button type="button" class="btn btn-danger">Danger</button>
      <button type="button" class="btn btn-warning">Warning</button>
      <button type="button" class="btn btn-info">Info</button>
      <button type="button" class="btn btn-light">Light</button>
      <button type="button" class="btn btn-dark">Dark</button>
      <button type="button" class="btn btn-link">Link</button>
    </div>
    <h2 class="h5">Outline</h2>
    <div class="d-flex flex-wrap gap-2 mb-3">
      <button type="button" class="btn btn-outline-primary">Primary</button>
      <button type="button" class="btn btn-outline-success">Success</button>
      <button type="button" class="btn btn-outline-danger">Danger</button>
      <button type="button" class="btn btn-outline-dark">Dark</button>
    </div>
    <h2 class="h5">Sizes and states</h2>
    <div class="d-flex flex-wrap align-items-center gap-2">
      <button type="button" class="btn btn-primary btn-lg">Large</button>
      <button type="button" class="btn btn-primary">Normal</button>
      <button type="button" class="btn btn-primary btn-sm">Small</button>
      <button type="button" class="btn btn-primary" disabled>Disabled</button>
      <a class="btn btn-outline-primary" href="#offers" role="button">Link styled as button</a>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Points to notice:

  • btn-outline-* buttons have a coloured border and fill with colour on hover. Use them for secondary actions next to a solid primary button.
  • btn-link looks like a link but keeps button spacing, useful for "Cancel" or "Skip".
  • btn-lg and btn-sm change the size.
  • The btn classes work on <button>, <a> and <input type="submit">.

Choosing the right element

Use <button> when something happens on this page (open a menu, submit a form, add to cart). Use <a href> when the user goes somewhere (another page). This is about meaning, not looks: screen readers announce them differently, and keyboards treat them differently.

Always write type="button" on buttons that are not meant to submit a form. A <button> inside a <form> defaults to type="submit", so a "Show password" button without a type would submit the form by accident.

Disabled buttons

A <button> is disabled with the disabled attribute: it turns pale, ignores clicks and cannot receive focus. An <a> has no disabled attribute, so for link buttons you add the disabled class, aria-disabled="true", and remove the href:

HTML
<button type="button" class="btn btn-success" disabled>Pay ₹1,299</button>

<a class="btn btn-secondary disabled" role="button" aria-disabled="true">Download invoice</a>

Full-width (block) buttons

On phones, wide buttons are easier to tap. Wrap the buttons in a d-grid container: grid children stretch to the full width, and gap-2 separates them. Add a responsive class to make them normal width on larger screens.

HTML
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
  <button class="btn btn-outline-secondary" type="button">Save draft</button>
  <button class="btn btn-primary" type="button">Publish</button>
</div>

On phones both buttons are full width and stacked. From md, d-md-flex puts them side by side, and justify-content-md-end moves them to the right.

Button groups and toolbars

A button group joins related buttons into one bar with shared borders. Give the wrapper btn-group, plus role="group" and an aria-label so screen readers announce what the group is for.

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Button groups</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-3">
    <p class="mb-1">View</p>
    <div class="btn-group mb-3" role="group" aria-label="Choose a view">
      <button type="button" class="btn btn-outline-primary active" aria-pressed="true">Day</button>
      <button type="button" class="btn btn-outline-primary">Week</button>
      <button type="button" class="btn btn-outline-primary">Month</button>
    </div>

    <p class="mb-1">Seat class (radio buttons styled as a group)</p>
    <div class="btn-group mb-3" role="group" aria-label="Seat class">
      <input type="radio" class="btn-check" name="seat" id="sl" autocomplete="off" checked>
      <label class="btn btn-outline-success" for="sl">Sleeper</label>
      <input type="radio" class="btn-check" name="seat" id="ac3" autocomplete="off">
      <label class="btn btn-outline-success" for="ac3">3A</label>
      <input type="radio" class="btn-check" name="seat" id="ac2" autocomplete="off">
      <label class="btn btn-outline-success" for="ac2">2A</label>
    </div>

    <p class="mb-1">Toolbar</p>
    <div class="btn-toolbar gap-2 mb-3" role="toolbar" aria-label="Text editor toolbar">
      <div class="btn-group btn-group-sm" role="group" aria-label="Formatting">
        <button type="button" class="btn btn-outline-secondary">Bold</button>
        <button type="button" class="btn btn-outline-secondary">Italic</button>
      </div>
      <div class="btn-group btn-group-sm" role="group" aria-label="Pages">
        <button type="button" class="btn btn-outline-secondary">1</button>
        <button type="button" class="btn btn-outline-secondary">2</button>
        <button type="button" class="btn btn-outline-secondary">3</button>
      </div>
    </div>

    <p class="mb-1">Toggle button</p>
    <button type="button" class="btn btn-outline-warning" data-bs-toggle="button" id="favBtn">Add to favourites</button>
    <p class="small mt-2" id="favState">Pressed: false</p>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
  <script>
    const fav = document.getElementById("favBtn");
    fav.addEventListener("click", () => {
      // Bootstrap toggles the "active" class and aria-pressed for us
      document.getElementById("favState").textContent = "Pressed: " + fav.getAttribute("aria-pressed");
    });
  </script>
</body>
</html>

What each part shows:

  • btn-group joins buttons. active marks the selected one, and aria-pressed="true" tells screen readers it is pressed.
  • Radio toggle buttons: each choice is a real <input type="radio"> with the btn-check class, followed by a <label> styled as a button. Only one can be checked, the keyboard arrow keys work, and the value is submitted with a form. Use type="checkbox" the same way for choices that can be combined.
  • btn-toolbar holds several groups, with gap-2 between them. btn-group-sm (or btn-group-lg) sizes a whole group at once.
  • data-bs-toggle="button" makes a single on/off button. Bootstrap's JavaScript adds or removes active and updates aria-pressed on each click.

Use btn-group-vertical instead of btn-group to stack the buttons in a column.

Close button

For dismissing alerts, modals and toasts, Bootstrap has a ready-made "X" button. It has no text, so it must have an aria-label:

HTML
<button type="button" class="btn-close" aria-label="Close"></button>

Advanced: custom button styles with CSS variables

Each button reads its colours and padding from CSS variables such as --bs-btn-bg, --bs-btn-color, --bs-btn-hover-bg and --bs-btn-padding-y. You can create your own variant without Sass by setting those variables in a class:

CSS
.btn-saffron {
  --bs-btn-bg: #ff9933;
  --bs-btn-color: #212529;
  --bs-btn-border-color: #ff9933;
  --bs-btn-hover-bg: #e6862b;
  --bs-btn-hover-border-color: #e6862b;
  --bs-btn-hover-color: #212529;
}

Then use it like any variant: class="btn btn-saffron".

Common mistakes

  • Missing the btn class. class="btn-primary" alone gives a blue colour but no button shape or padding.
  • No type on buttons inside forms. The button submits the form. Add type="button" to buttons that do something else.
  • <div> or <span> styled as buttons. They cannot be focused with the Tab key or pressed with Enter. Use <button>.
  • Icon-only buttons without a label. Add an aria-label such as aria-label="Delete order".

Key points

  • A button needs btn plus a variant such as btn-primary, btn-outline-danger or btn-link.
  • btn-lg and btn-sm change size; disabled (or the disabled class plus aria-disabled on links) disables.
  • Wrap buttons in d-grid gap-2 for full-width buttons; btn-block no longer exists.
  • btn-group joins related buttons, btn-toolbar holds groups, and btn-check inputs make radio or checkbox buttons.
  • Use <button type="button"> for actions and <a href> for navigation.

Exercise

Create a food-ordering panel for a Pune canteen. Add a radio button group to choose a portion size (Half, Full, Family) with Full checked, a checkbox-style toggle group for extras (Raita, Papad, Sweet), and at the bottom a full-width "Place order" button on phones that becomes a normal right-aligned button on medium screens, next to an outline "Cancel" button.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Canteen order</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-3">
    <p class="mb-1">Portion</p>
    <div class="btn-group mb-3" role="group" aria-label="Portion size">
      <input type="radio" class="btn-check" name="portion" id="half" autocomplete="off">
      <label class="btn btn-outline-primary" for="half">Half</label>
      <input type="radio" class="btn-check" name="portion" id="full" autocomplete="off" checked>
      <label class="btn btn-outline-primary" for="full">Full</label>
      <input type="radio" class="btn-check" name="portion" id="family" autocomplete="off">
      <label class="btn btn-outline-primary" for="family">Family</label>
    </div>
    <p class="mb-1">Extras</p>
    <div class="btn-group mb-4" role="group" aria-label="Extras">
      <input type="checkbox" class="btn-check" id="raita" autocomplete="off">
      <label class="btn btn-outline-secondary" for="raita">Raita</label>
      <input type="checkbox" class="btn-check" id="papad" autocomplete="off">
      <label class="btn btn-outline-secondary" for="papad">Papad</label>
      <input type="checkbox" class="btn-check" id="sweet" autocomplete="off">
      <label class="btn btn-outline-secondary" for="sweet">Sweet</label>
    </div>
    <div class="d-grid gap-2 d-md-flex justify-content-md-end">
      <button type="button" class="btn btn-outline-secondary">Cancel</button>
      <button type="button" class="btn btn-success">Place order</button>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Radio inputs sharing name="portion" allow only one choice, while the checkboxes can be combined. The d-grid wrapper makes full-width buttons on phones, and d-md-flex justify-content-md-end lines them up on the right from tablets.

Quick Quiz

1. Which classes make an outlined red button?

2. Bootstrap 5 removed btn-block. How do you make full-width buttons now?

3. What does the btn-check class go on?

4. Why add type="button" to a "Show password" button inside a form?

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