Bootstrap Empty State Placeholder — Free HTML CSS JS Snippet
Bootstrap Empty State Placeholder · Layouts · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="container py-5 d-flex justify-content-center">
<div class="card bses-card shadow-sm">
<div class="card-body p-4">
<div class="d-flex justify-content-end mb-2">
<button class="btn btn-sm btn-outline-secondary" id="besToggle">Toggle demo state</button>
</div>
<div id="besEmpty" class="text-center py-5">
<svg width="72" height="72" viewBox="0 0 24 24" fill="none" class="mb-3 text-secondary mx-auto d-block">
<path d="M3 7h18M3 7l1.5 12a2 2 0 0 0 2 1.8h11a2 2 0 0 0 2-1.8L21 7M3 7l2-4h14l2 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M9 11v4M15 11v4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<h5 class="fw-bold mb-1">Your cart is empty</h5>
<p class="text-muted mb-4">Items you add will show up here.</p>
<button class="btn btn-primary" id="besCta">Browse products</button>
</div>
<div id="besPopulated" class="d-none">
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>Wireless Mouse</span><span class="text-muted small">$24.00</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>Mechanical Keyboard</span><span class="text-muted small">$89.00</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>USB-C Hub</span><span class="text-muted small">$34.00</span>
</li>
</ul>
</div>
</div>
</div>
</div>.bses-card { width: 380px; border-radius: 14px; border: 1px solid #eceef1; }const toggleBtn = document.getElementById('besToggle');
const empty = document.getElementById('besEmpty');
const populated = document.getElementById('besPopulated');
const cta = document.getElementById('besCta');
function setEmpty(isEmpty) {
empty.classList.toggle('d-none', !isEmpty);
populated.classList.toggle('d-none', isEmpty);
toggleBtn.textContent = isEmpty ? 'Show populated view' : 'Show empty state';
}
let currentlyEmpty = true;
toggleBtn.addEventListener('click', () => {
currentlyEmpty = !currentlyEmpty;
setEmpty(currentlyEmpty);
});
// The CTA button inside the empty state itself also switches to the
// populated view, simulating what happens after a user adds an item.
cta.addEventListener('click', () => {
currentlyEmpty = false;
setEmpty(false);
});Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Bootstrap Empty State Placeholder — HTML, CSS & JavaScript

An empty state is the screen most product teams forget to design on purpose, so this snippet treats it as a first-class, reusable block rather than an afterthought: a centered inline SVG icon (a simple outlined cart/box drawn with path elements and stroke rather than a raster image, so it scales crisply and inherits currentColor from the surrounding text-secondary class), a bold heading, a muted one-line message, and a primary Bootstrap btn btn-primary call-to-action, all centered inside a real Bootstrap card. Using an inline SVG instead of an <img> means the icon needs zero extra network requests and can be recolored purely through CSS currentColor inheritance, which matters because empty states often need to match a theme's muted-secondary palette rather than a fixed icon color.
The demo itself is built to prove the component actually works both ways, not just render once: a "Toggle demo state" button and the CTA button (#besCta) inside the empty state both call a single setEmpty(isEmpty) function that toggles Bootstrap's own d-none utility class on the #besEmpty and #besPopulated blocks in lockstep, so exactly one is ever visible, and also relabels the toggle button itself to describe the action it will perform next rather than the state it is currently in. A currentlyEmpty boolean tracks state outside the DOM so both entry points (the manual toggle and the CTA click) can flip it consistently without reading the current visibility from the DOM each time.
The non-obvious detail worth calling out is that the CTA button living *inside* the empty state is wired to the same state-changing function as the external toggle button, rather than having its own separate logic — in a real cart or list this is exactly the button a user clicks to leave the empty state, so testing it needs to produce the same populated view as the demo toggle, not a different one. Keeping both entry points calling one shared function also means a future change to what "populated" looks like — say, adding a badge count — only needs to happen in one place.
Because the whole component is two sibling blocks toggled by one boolean, it maps directly onto conditional rendering in any component framework: an isEmpty state variable controlling which JSX/template block renders, with the underlying SVG, heading, and button markup unchanged.
The populated view reuses Bootstrap's own list-group-flush and d-flex justify-content-between utilities to align each item's name on the left and its price on the right without any custom flexbox rules of its own, which keeps the "after" state visually consistent with the rest of a typical Bootstrap-built page rather than looking like a bolted-on demo table.
Step by step
How to Use
- 1Load the snippetThe preview shows a card with a centered outline icon, "Your cart is empty" heading, a short message, and a "Browse products" button.
- 2Click "Toggle demo state"The empty state disappears and a populated list of three cart items with prices appears in its place.
- 3Notice the toggle button relabelsIt now reads "Show empty state" so its next click is predictable.
- 4Click it againThe view switches back to the empty state with the icon, heading, and CTA button.
- 5Click "Browse products" inside the empty stateThe same populated list view appears, proving the CTA is wired to the real state change, not just a decorative button.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Yes. Store an isEmpty boolean in useState (React), a ref (Vue), or a component property (Angular), and conditionally render the empty-state JSX/template or the populated one from it; the CTA button and the toggle button both just flip that one variable, exactly like the shared setEmpty() function does here.
An inline SVG has no network request, scales without blurring at any size, and its stroke color inherits from the surrounding text-secondary class via currentColor, so it automatically matches the empty state's muted tone without a separate color override.
Yes — it calls the same setEmpty(false) logic as the external toggle button, switching to the populated view, which mirrors what a real "Browse products" or "Add your first item" button should do in production.
Yes — swap the SVG path, heading text, message, and CTA label; the toggle logic and d-none-based visibility switching stay identical regardless of what the empty and populated content actually is.
Yes — replace the Bootstrap card and btn classes with Tailwind equivalents like a bordered rounded-xl container and a bg-blue-600 button; the inline SVG and the isEmpty toggle logic need no changes at all.
A dedicated currentlyEmpty boolean lets both the toggle button and the CTA button update state through one function call without each needing to inspect and interpret the other element's current CSS classes, keeping the two entry points from drifting out of sync.