This is a debate I have with myself on every Bootstrap project. Let me share the framework I've landed on after building dozens of Bootstrap apps.
Bootstrap 5 Utilities Are Genuinely Good
Bootstrap 5's utility classes cover a lot of ground. Spacing, flexbox, grid, colors, typography, display, positioning, borders, shadows — you can build surprisingly complete layouts without a single line of custom CSS.
<!-- This is entirely utilities — and it's fine --> <div class="d-flex align-items-center gap-3 p-4 bg-white rounded-3 shadow-sm"> <div class="rounded-circle bg-primary text-white d-flex align-items-center justify-content-center fw-bold" style="width:48px;height:48px;">GS</div> <div> <h6 class="fw-bold mb-0">Gagan Singh</h6> <p class="text-muted small mb-0">Founder</p> </div> <a href="#" class="btn btn-sm ms-auto text-white" style="background:#fd4766;">Follow</a> </div>
Is that HTML verbose? Yes. Does it work without touching a stylesheet? Also yes. For a component you'll only use once or twice, that tradeoff is often worth it.
When Utilities Get Messy
The problem hits when you need the same combination of classes repeatedly. Five cards all with card border-0 shadow-sm rounded-3 overflow-hidden — that's fine. But when you have thirty cards and you want to change the shadow on all of them, you're doing thirty find-and-replaces.
The other problem is complex visual states that utilities can't express:
/* You can't do this with Bootstrap utilities alone */ .nav-link::after { content: ''; position: absolute; bottom: -2px; left: 0; right: 0; height: 2px; background: #fd4766; transform: scaleX(0); transition: transform 0.2s ease; } .nav-link:hover::after, .nav-link.active::after { transform: scaleX(1); }
Pseudo-elements, complex transitions, custom clip-paths, CSS animations — these all require custom CSS.
My Working Rules
Use Bootstrap utilities for:
- All spacing (margin, padding, gap) —
mt-3,px-4,gap-2 - Display and flex —
d-flex,align-items-center,justify-content-between - Width/height shortcuts —
w-100,h-100,min-vh-100 - Color and background —
text-muted,bg-white,text-success - Typography —
fw-bold,small,fs-5,text-truncate - Borders and radius —
border-0,rounded-3,shadow-sm - Visibility and position —
d-none d-md-block,position-relative
Write custom CSS for:
- Reusable component styles used more than 3 times
- Hover effects with transitions
- Pseudo-elements (::before, ::after)
- CSS animations and keyframes
- Custom scrollbar styling
- Anything requiring more than 5-6 utilities to express
- Brand-specific overrides of Bootstrap components
The Hybrid Approach
The cleanest codebases I've worked on mix both:
// Custom class for reusable pattern .template-card { border-radius: 12px; overflow: hidden; transition: transform 0.25s ease, box-shadow 0.25s ease; &:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.12); } }
<!-- Bootstrap utilities handle layout, custom class handles the specific pattern --> <div class="template-card border-0 shadow-sm"> <img src="..." class="w-100" alt=""> <div class="p-3"> <!-- content --> </div> </div>
The component class captures the specific, reusable pattern. Utilities handle the generic layout needs.
The Bootstrap 5 Utility API
One thing people miss: Bootstrap 5 has a Utility API that lets you generate your own utility classes from Sass. Need .cursor-pointer? Add it to the utility map:
$utilities: map-merge( $utilities, ( "cursor": ( property: cursor, values: auto pointer default grab ) ) );
Now you have .cursor-pointer, .cursor-grab etc. This bridges the gap between utility-first and custom CSS — you get new utilities without leaving Bootstrap's system.
Bottom Line
Neither approach in isolation. Use utilities aggressively for layout and spacing. Write custom CSS when you have a genuine component pattern that repeats or when you need CSS features that utilities can't express. Extract repeated utility patterns into named classes. Use Bootstrap's Utility API for common gaps.
The developers who write the most maintainable Bootstrap code are the ones who know intuitively which tier to reach for — and switch between them without ideology.
Frequently Asked Questions
Related Comparisons
Already Decided on Bootstrap?
Get a complete Angular 21 + Bootstrap 5 admin dashboard template — production ready.
Browse Templates →Use code FIRST30 for 30% off.