Bootstrap 5 Counter
A Counter component in Bootstrap 5 displays animated numeric statistics that increment from zero to a target value when scrolled into view, drawing attention to key metrics. It typically combines large typographic numbers with short supporting labels, making it ideal for communicating impact at a glance. Use it on landing pages, about pages, or anywhere you need to translate data into a persuasive visual narrative.
Primary Class
.counterCommon Use Cases
- →SaaS product pages showing metrics like '14,000+ active users', 'Uptime 99.9%', or 'Support tickets resolved in under 2 hours'
- →Nonprofit and charity sites displaying fundraising totals, number of people helped, or volunteer hours logged to build donor trust
- →Agency and portfolio pages highlighting completed projects, years of experience, or client satisfaction scores to establish credibility
- →E-commerce stores featuring real-time or cumulative stats such as orders shipped, countries served, or five-star reviews to reduce purchase hesitation
Variants & Classes
| Variant | Description |
|---|---|
| Counter Default | Standard counter with Bootstrap's default styling. |
| Counter Responsive | Responsive variant that adapts to different screen sizes. |
Code Example
<section class="py-5 bg-light">
<div class="container">
<div class="row text-center g-4">
<div class="col-6 col-md-3">
<div class="p-4">
<h2 class="display-4 fw-bold text-primary counter" data-target="14200">0</h2>
<p class="text-muted mb-0">Active Users</p>
</div>
</div>
<div class="col-6 col-md-3">
<div class="p-4">
<h2 class="display-4 fw-bold text-primary counter" data-target="99">0</h2>
<p class="text-muted mb-0">% Uptime SLA</p>
</div>
</div>
<div class="col-6 col-md-3">
<div class="p-4">
<h2 class="display-4 fw-bold text-primary counter" data-target="380">0</h2>
<p class="text-muted mb-0">Projects Shipped</p>
</div>
</div>
<div class="col-6 col-md-3">
<div class="p-4">
<h2 class="display-4 fw-bold text-primary counter" data-target="4800">0</h2>
<p class="text-muted mb-0">5-Star Reviews</p>
</div>
</div>
</div>
</div>
</section>
<script>
const counters = document.querySelectorAll('.counter');
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const el = entry.target;
const target = +el.dataset.target;
const duration = 1800;
const step = Math.ceil(target / (duration / 16));
let current = 0;
const timer = setInterval(() => {
current = Math.min(current + step, target);
el.textContent = current.toLocaleString();
if (current >= target) clearInterval(timer);
}, 16);
observer.unobserve(el);
}
});
}, { threshold: 0.4 });
counters.forEach(c => observer.observe(c));
</script>Live Examples
Basic Counter
Canvas Framework Variants
The Canvas template extends Bootstrap 5 with 1,658+ component variants. Generate any of these using Canvas Builder:
- ✓Canvas Builder generated counter with custom colours
- ✓Counter with interactive states
- ✓Responsive counter for all screen sizes
Best Practices
Use IntersectionObserver, not scroll events
Trigger counter animations only when the section enters the viewport using IntersectionObserver with a threshold of around 0.3–0.5 — this avoids janky scroll listeners and ensures users actually see the animation play out rather than catching it mid-count.
Format large numbers with toLocaleString()
Call Number.toLocaleString() on each incremented value so '14200' renders as '14,200' automatically, adapting to the user's locale — this prevents raw unformatted numbers that look like a bug rather than a statistic.
Pair numbers with icons, not just labels
Adding a Bootstrap Icons icon above each counter (e.g. bi-people-fill for users, bi-star-fill for reviews) gives each stat visual context instantly, reducing cognitive load before the number finishes animating.
Cap animation duration between 1.5s and 2.5s
Animations shorter than 1.5 seconds feel like a flicker on large numbers, while anything over 2.5 seconds loses the viewer's attention — store duration as a data attribute so each counter block can have an individually tuned speed without touching JS.