Bootstrap Icons
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:
<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:
npm install bootstrap-icons// 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:
<!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-1on the paragraph makes the icons big, because they inherit the font size. You can also setfont-sizeon the icon itself. - Colour:
text-success,text-dangerand the other text colour classes colour icons. - Spacing:
me-1orme-2separates 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, andicon-link-hovernudges 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:
| Situation | What to do |
|---|---|
| The icon only decorates text that says the same thing | Add aria-hidden="true" to the icon |
| The icon is the only content of a button or link | aria-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:
<!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>:
<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.
| Method | Best for |
|---|---|
| Icon font | Quick setup, many icons, easy sizing and colouring |
| Inline SVG | A few icons, best sharpness, no extra download |
| SVG sprite | Many 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:bisets the font,bi-cartpicks 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-iconsfrom npm. - With the icon font, write
<i class="bi bi-{name}"></i>; icons inherit text size and colour, sofs-*andtext-*work. - Hide decorative icons with
aria-hidden="true"; give icon-only buttons anaria-label; give meaningful iconsrole="img"and a label. - Inline SVG, an SVG sprite or
<img>files avoid loading the whole font. icon-linkneatly 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
<!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?
bi applies the icon font and bi-cart chooses the icon.
2. How do you make an icon font icon larger and red?
Font icons behave like text, so text utilities change their size and colour.
3. A delete button contains only a trash icon. What makes it accessible?
The button needs an accessible name; the decorative icon is hidden from screen readers.
4. Which icon library did Bootstrap 3 include?
Bootstrap 3 bundled Glyphicons; Bootstrap 4 dropped it, and Bootstrap Icons arrived later as the official library.
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
