Bootstrap Spinners
Spinners indicate that content is loading or an action is processing. This lesson covers the two spinner styles, sizing, color variants, and accessible usage inside buttons.
Bootstrap Spinners Overview
Bootstrap offers two spinner styles: .spinner-border, a rotating ring, and .spinner-grow, a pulsing dot that grows and fades. Both accept the same theme color classes (text-primary, text-success, and so on) used elsewhere in Bootstrap, and both include a visually-hidden 'Loading...' span by default for screen reader users.
Spinners are commonly placed inside buttons to indicate an in-progress action, combined with the disabled attribute to prevent duplicate submissions, or sized down with spinner-border-sm and spinner-grow-sm for more compact contexts.
| Concept | Description | Common Usage |
| .spinner-border | Rotating ring loading indicator | General-purpose loading state |
| .spinner-grow | Pulsing, growing dot indicator | Alternative, softer loading style |
| spinner-*-sm | Smaller sized spinner variant | Inline use inside buttons or small UI |
| text-{color} on spinners | Theme color applied to the spinner | Matching spinner color to surrounding UI |
| visually-hidden loading text | Screen-reader-only status text | Accessible loading announcement |
Bootstrap Spinners Example
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<button class="btn btn-primary" type="button" disabled>
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
Saving...
</button>
<div class="spinner-grow text-success" role="status">
<span class="visually-hidden">Loading...</span>
</div>
The first spinner is a standalone loading ring with visually-hidden text describing its purpose for screen readers. The button example combines a small spinner-border-sm with the disabled attribute and visible 'Saving...' text to clearly communicate an in-progress action. The final example shows the alternative spinner-grow style with a success color.
Top 10 Bootstrap Spinners Examples
These are the spinner patterns you'll use to indicate loading and processing states.
| # | Example | Syntax |
| 1 | Basic border spinner | <div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div> |
| 2 | Growing spinner | <div class="spinner-grow" role="status"><span class="visually-hidden">Loading...</span></div> |
| 3 | Small spinner | <div class="spinner-border spinner-border-sm"></div> |
| 4 | Colored spinner | <div class="spinner-border text-success"></div> |
| 5 | Spinner inside a button | <button class="btn btn-primary" disabled><span class="spinner-border spinner-border-sm"></span> Loading</button> |
| 6 | Spinner with custom size | <div class="spinner-border" style="width: 3rem; height: 3rem;"></div> |
| 7 | Centered page spinner | <div class="d-flex justify-content-center"><div class="spinner-border"></div></div> |
| 8 | Spinner with aria-hidden icon usage | <span class="spinner-border spinner-border-sm" aria-hidden="true"></span> |
| 9 | Multiple color spinners row | <div class="spinner-grow text-warning"></div> |
| 10 | Full-page loading overlay spinner | <div class="position-fixed top-50 start-50 translate-middle"><div class="spinner-border text-primary"></div></div> |
Popular Real-World Bootstrap Spinners Examples
These spinner patterns commonly appear during form submissions, data fetching, and page transitions.
| Scenario | Pattern |
| Submit button while saving | <button class="btn btn-primary" disabled><span class="spinner-border spinner-border-sm"></span> Saving...</button> |
| Full-page initial load spinner | <div class="d-flex justify-content-center py-5"><div class="spinner-border text-primary"></div></div> |
| Table data loading state | <td colspan="4" class="text-center"><div class="spinner-border spinner-border-sm"></div></td> |
| Infinite scroll loading indicator | <div class="text-center py-3"><div class="spinner-grow text-secondary"></div></div> |
| Image lazy-load placeholder | <div class="spinner-border text-muted" role="status"></div> |
| Search results loading | <div class="spinner-border spinner-border-sm text-primary"></div> Searching... |
| File upload processing indicator | <span class="spinner-border spinner-border-sm" aria-hidden="true"></span> Uploading... |
| Dashboard widget refresh spinner | <div class="spinner-border spinner-border-sm text-secondary"></div> |
Best Practices
- Always include a visually-hidden 'Loading...' span (or similar) inside standalone spinners.
- Disable buttons while their spinner is visible to prevent duplicate submissions.
- Match spinner color to the surrounding button or theme context using text-{color} classes.
- Use spinner-border-sm inside compact UI like buttons, and full-size spinners for page-level loading.
- Pair a spinner with visible text ('Saving...', 'Loading...') for extra clarity beyond the animation alone.
- Center full-page spinners using flex utilities instead of custom positioning CSS.
- Remove or hide the spinner promptly once the underlying action completes.
Common Mistakes
- Leaving out the visually-hidden loading text, so screen readers announce nothing meaningful.
- Forgetting to disable the button while a spinner shows, allowing repeated clicks and duplicate requests.
- Using spinner-grow and spinner-border inconsistently across similar loading states in the same app.
- Not resetting the button back to its normal state if the request fails.
- Using an oversized spinner inside a small button, breaking the layout.
- Relying solely on a spinner with no fallback timeout message for very slow requests.
Key Takeaways
- spinner-border and spinner-grow are Bootstrap's two built-in loading indicator styles.
- Sizing modifiers and theme colors adapt spinners to buttons or full-page contexts.
- Accessible spinners always include a visually-hidden status text.
- Disabling the triggering button while a spinner shows prevents duplicate actions.
- Spinners should be removed promptly once the underlying task finishes or fails.
Pro Tip
For network requests that might take a while, pair the spinner with a timeout-based fallback message like 'Still working...' after a few seconds, so users don't assume the app has frozen.