Bootstrap Icons

Bootstrap · Lesson 24 of 28 7 min read Updated

Icons make interfaces faster to scan: a cart for the shopping cart, a bin for delete, a phone for "call us". Bootstrap Icons is the official icon library from the Bootstrap team: more than 2,000 free icons drawn in the same style, licensed under MIT. It is a separate project, so you add it in addition to Bootstrap (and you can even use it without Bootstrap).

In this lesson you will add the icons with a CDN link or npm, use them as a font and as SVG, style them, and make sure screen-reader users are not confused by them. The current version is Bootstrap Icons 1.13.2.

Adding Bootstrap Icons

The quickest way is the icon font stylesheet from a CDN, placed in the <head> next to Bootstrap's CSS:

HTML
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css" rel="stylesheet">

In an npm and Vite project (see Get Started), install the package and import its CSS in your JavaScript entry file. Vite copies the font files for you:

Terminal
npm install bootstrap-icons
JavaScript
// src/main.js
import "bootstrap-icons/font/bootstrap-icons.css";

To find an icon, search on icons.getbootstrap.com. Every icon has a name, such as cart, heart-fill, telephone or geo-alt, and many have -fill versions.

Using the icon font

With the font, an icon is an empty <i> (or <span>) element with two classes: bi and bi-{name}. Because the icon is a font character, it takes the size and colour of the text around it, so all your text utilities work:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Bootstrap Icons</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-4">
    <p class="fs-1">
      <i class="bi bi-house-door" aria-hidden="true"></i>
      <i class="bi bi-cart3 text-success" aria-hidden="true"></i>
      <i class="bi bi-heart-fill text-danger" aria-hidden="true"></i>
      <i class="bi bi-star-fill text-warning" aria-hidden="true"></i>
    </p>

    <ul class="list-unstyled">
      <li class="mb-2"><i class="bi bi-geo-alt-fill text-primary me-2" aria-hidden="true"></i>MG Road, Kochi 682016</li>
      <li class="mb-2"><i class="bi bi-telephone-fill text-primary me-2" aria-hidden="true"></i>0484 123 4567</li>
      <li class="mb-2"><i class="bi bi-clock-fill text-primary me-2" aria-hidden="true"></i>Open 10 am to 9 pm</li>
    </ul>

    <div class="d-flex flex-wrap gap-2 mb-3">
      <button type="button" class="btn btn-success"><i class="bi bi-cart-plus me-1" aria-hidden="true"></i>Add to cart</button>
      <button type="button" class="btn btn-outline-danger" aria-label="Delete order"><i class="bi bi-trash" aria-hidden="true"></i></button>
      <button type="button" class="btn btn-outline-secondary btn-sm"><i class="bi bi-download me-1" aria-hidden="true"></i>Invoice</button>
    </div>

    <p class="mb-1">Rating:
      <span role="img" aria-label="Rated 4.5 out of 5" class="text-warning">
        <i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-fill"></i><i class="bi bi-star-half"></i>
      </span>
    </p>

    <a class="icon-link icon-link-hover" href="#menu">
      See the full menu
      <i class="bi bi-arrow-right" aria-hidden="true"></i>
    </a>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

What the example shows:

  • Size: fs-1 on the paragraph makes the icons big, because they inherit the font size. You can also set font-size on the icon itself.
  • Colour: text-success, text-danger and the other text colour classes colour icons.
  • Spacing: me-1 or me-2 separates an icon from the text after it.
  • Icon with text: the "Add to cart" icon is decoration, because the words already say it all.
  • Icon-only button: the delete button has no words, so the button gets an aria-label.
  • icon-link (a Bootstrap 5.3 helper) lines up text and icon in a link, and icon-link-hover nudges the arrow when hovered.

Accessibility: decorative or meaningful?

Screen readers may try to read icon font characters as strange symbols, or skip them. Decide for each icon:

SituationWhat to do
The icon only decorates text that says the same thingAdd aria-hidden="true" to the icon
The icon is the only content of a button or linkaria-hidden="true" on the icon, and aria-label on the button or link
The icon carries information by itself (a rating, a status)Wrap it with role="img" and an aria-label that states the information

The rating in the example is announced as "Rated 4.5 out of 5", not as five unknown symbols. Also check colour contrast: a pale yellow star on white is hard to see for many users.

Using SVG instead of the font

The icon font loads every icon (a font file of about 130 KB), even if you use only five. SVG avoids that and draws more sharply. There are three ways:

1. Inline SVG. Copy the icon's SVG code from its page on the icons site and paste it into your HTML. fill="currentColor" makes it follow the text colour, just like the font:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Inline SVG icon</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">
    <button type="button" class="btn btn-outline-danger" id="likeBtn" aria-pressed="false">
      <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-heart-fill me-1" viewBox="0 0 16 16" aria-hidden="true">
        <path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314"/>
      </svg>
      <span id="likeText">Like</span>
    </button>
    <p class="mt-3 text-danger" style="font-size: 3rem">
      <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" role="img" aria-label="Favourite">
        <path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314"/>
      </svg>
    </p>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
  <script>
    const like = document.getElementById("likeBtn");
    like.addEventListener("click", () => {
      const on = like.getAttribute("aria-pressed") !== "true";
      like.setAttribute("aria-pressed", String(on));
      like.classList.toggle("btn-danger", on);
      like.classList.toggle("btn-outline-danger", !on);
      document.getElementById("likeText").textContent = on ? "Liked" : "Like";
    });
  </script>
</body>
</html>

The second heart uses width="1em" height="1em", so it scales with the font size of its parent (3rem here). It is meaningful on its own, so it has role="img" and an aria-label.

2. SVG sprite. The package includes bootstrap-icons.svg, a single file containing every icon. Reference one icon by its name with <use>:

HTML
<svg class="bi" width="24" height="24" fill="currentColor" aria-hidden="true">
  <use href="/assets/bootstrap-icons.svg#truck"></use>
</svg>

The sprite must be served from your own site (copy it from node_modules/bootstrap-icons/), because browsers do not load <use> references from another domain.

3. Image file. Each icon also exists as its own file in the package's icons/ folder, so <img src="/assets/icons/truck.svg" alt="Delivery" width="32" height="32"> works too, but an <img> cannot change colour with CSS.

MethodBest for
Icon fontQuick setup, many icons, easy sizing and colouring
Inline SVGA few icons, best sharpness, no extra download
SVG spriteMany icons with one cached file
<img>Simple, fixed-colour images

Common mistakes

  • Forgetting the icons stylesheet. The <i class="bi bi-cart"> elements stay empty. Bootstrap's own CSS does not include the icons.
  • Writing only bi-cart. Both classes are needed: bi sets the font, bi-cart picks the icon.
  • Icon-only buttons with no label. A screen reader says "button" and nothing else. Add aria-label.
  • Using icons as the only sign of a status. A red cross might mean "failed" to one user and "close" to another. Add text.

Key points

  • Bootstrap Icons (1.13.2, over 2,000 icons, MIT licence) is a separate library: add its stylesheet or install bootstrap-icons from npm.
  • With the icon font, write <i class="bi bi-{name}"></i>; icons inherit text size and colour, so fs-* and text-* work.
  • Hide decorative icons with aria-hidden="true"; give icon-only buttons an aria-label; give meaningful icons role="img" and a label.
  • Inline SVG, an SVG sprite or <img> files avoid loading the whole font.
  • icon-link neatly aligns an icon with link text in Bootstrap 5.3.

Exercise

Create a "Contact us" card for a bakery in Pune. Inside the card body list the address, phone, email and opening hours, each with a matching Bootstrap Icon in the primary colour. At the bottom add three icon-only social buttons (Instagram, YouTube, Facebook) with proper labels, and a "Get directions" link using icon-link with an arrow icon.

Show solution
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Contact us</title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css" rel="stylesheet">
</head>
<body>
  <div class="container py-4" style="max-width: 460px">
    <div class="card">
      <div class="card-body">
        <h1 class="card-title h5">Deccan Bakers, Pune</h1>
        <ul class="list-unstyled">
          <li class="mb-2"><i class="bi bi-geo-alt-fill text-primary me-2" aria-hidden="true"></i>FC Road, Shivajinagar</li>
          <li class="mb-2"><i class="bi bi-telephone-fill text-primary me-2" aria-hidden="true"></i>020 1234 5678</li>
          <li class="mb-2"><i class="bi bi-envelope-fill text-primary me-2" aria-hidden="true"></i>orders@example.com</li>
          <li class="mb-3"><i class="bi bi-clock-fill text-primary me-2" aria-hidden="true"></i>8 am to 10 pm, all days</li>
        </ul>
        <div class="d-flex gap-2 mb-3">
          <a class="btn btn-outline-secondary" href="#instagram" aria-label="Instagram"><i class="bi bi-instagram" aria-hidden="true"></i></a>
          <a class="btn btn-outline-secondary" href="#youtube" aria-label="YouTube"><i class="bi bi-youtube" aria-hidden="true"></i></a>
          <a class="btn btn-outline-secondary" href="#facebook" aria-label="Facebook"><i class="bi bi-facebook" aria-hidden="true"></i></a>
        </div>
        <a class="icon-link icon-link-hover" href="#map">Get directions <i class="bi bi-arrow-right" aria-hidden="true"></i></a>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Each list icon is decorative because the text says the same thing, so it has aria-hidden="true". The social links contain only icons, so the links carry the aria-label.

Quick Quiz

1. Which two classes show the Bootstrap Icons cart icon with the icon font?

2. How do you make an icon font icon larger and red?

3. A delete button contains only a trash icon. What makes it accessible?

4. Which icon library did Bootstrap 3 include?

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