Bootstrap Spinners

Spinners show users that something is loading. Bootstrap 5 provides two ready-made styles built entirely with HTML and CSS: a rotating border spinner and a pulsing grow spinner. No JavaScript is required to animate them.

Border and grow spinners

The most common spinner is the border spinner, created with the .spinner-border class. It looks like a spinning ring. The alternative is the grow spinner, created with .spinner-grow, which repeatedly fades in and out. Both are simple <div> elements, and both should include a visually hidden label so screen reader users know content is loading.

The two spinner types

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="spinner-border" role="status">
  <span class="visually-hidden">Loading...</span>
</div>

<div class="spinner-grow" role="status">
  <span class="visually-hidden">Loading...</span>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
Note: The role="status" attribute plus a visually-hidden label is important for accessibility. Without it, a screen reader has no way to announce that the page is busy.

Coloring spinners

Spinners take their color from the current text color, so you color them with text utilities like text-primary or text-danger rather than background utilities. This applies to both the border and grow styles.

Colored spinners

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="spinner-border text-primary" role="status"></div>
<div class="spinner-border text-success" role="status"></div>
<div class="spinner-grow text-danger" role="status"></div>
<div class="spinner-grow text-warning" role="status"></div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
  • Use text-* utilities to color a spinner, not bg-* utilities.
  • spinner-border-sm and spinner-grow-sm make smaller spinners.
  • Wrap a spinner in a sized element or add margin utilities to position it.
  • Always include role="status" and a visually hidden label.

Sizing and spinners inside buttons

Add the -sm modifier class to shrink a spinner. A very common pattern is placing a small spinner inside a button to show that a form is submitting. You disable the button while it loads so the user cannot click it twice.

Small spinner and a loading button

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

<div class="spinner-border spinner-border-sm text-secondary" role="status"></div>

<button class="btn btn-primary" type="button" disabled>
  <span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
  Saving...
</button>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Spinner classes at a glance

ClassEffect
spinner-borderRotating ring spinner
spinner-growPulsing / growing spinner
spinner-border-smSmaller border spinner
spinner-grow-smSmaller grow spinner
text-primary, text-danger, ...Sets the spinner color

Inside a button, set aria-hidden="true" on the spinner because the button's own text (like "Saving...") already tells the user what is happening, so the spinner does not need to be announced separately.

Exercise: Bootstrap Spinners

What are the two visual spinner styles Bootstrap 5 provides out of the box?