Bootstrap Data Table Error State — Free HTML CSS JS Snippet
Bootstrap Data Table Error State · Tables · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="container py-5 d-flex justify-content-center">
<div class="card bstblerr-card">
<div class="card-body p-3">
<h6 class="fw-bold mb-2">Recent orders</h6>
<table class="table table-sm align-middle mb-0">
<thead>
<tr><th>Order</th><th>Customer</th><th>Total</th><th>Status</th></tr>
</thead>
<tbody id="bstblerrBody"></tbody>
</table>
</div>
</div>
</div>.bstblerr-card { width: 460px; max-width: 100%; border: 1px solid #eceef1; border-radius: 14px; }
.bstblerr-loading td { color: #9ca3af; font-size: 13px; }
.bstblerr-error-icon { font-size: 20px; }const body = document.getElementById('bstblerrBody');
let attempt = 0;
const ROWS = [
['#1042', 'Dana Reyes', '$128.00', 'Shipped', 'success'],
['#1043', 'Marcus Lee', '$64.50', 'Processing', 'secondary'],
['#1044', 'Priya Nair', '$212.00', 'Delivered', 'success'],
];
function renderLoading() {
body.innerHTML = '<tr class="bstblerr-loading"><td colspan="4" class="text-center py-4">Loading orders...</td></tr>';
}
function renderError(message) {
body.innerHTML =
'<tr><td colspan="4" class="text-center py-4">' +
'<div class="bstblerr-error-icon mb-1">⚠</div>' +
'<p class="fw-semibold mb-1">Couldn\'t load orders</p>' +
'<p class="small text-muted mb-3">' + message + '</p>' +
'<button type="button" class="btn btn-sm btn-outline-danger" id="bstblerrRetry">Retry</button>' +
'</td></tr>';
document.getElementById('bstblerrRetry').addEventListener('click', load);
}
function renderData() {
body.innerHTML = ROWS.map(([id, name, total, status, tone]) =>
'<tr><td>' + id + '</td><td>' + name + '</td><td>' + total +
'</td><td><span class="badge text-bg-' + tone + '">' + status + '</span></td></tr>'
).join('');
}
function load() {
attempt++;
renderLoading();
setTimeout(() => {
// The first attempt always fails so the error state (and Retry) is
// actually reachable in this preview; every retry after that has a
// genuine 60% chance of succeeding, like a flaky real request.
const failed = attempt === 1 || Math.random() > 0.6;
if (failed) {
renderError('Network request failed. Check your connection and try again.');
} else {
renderData();
}
}, 1000);
}
load();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 Data Table Error State — HTML, CSS & JavaScript

A table's error state is easy to get structurally wrong by replacing the whole table, headers included — this snippet only ever replaces #bstblerrBody, so the column headers (Order, Customer, Total, Status) stay put whether the table is loading, showing an error, or showing real rows. The error message itself lives in a single <tr> with one <td colspan="4">, which keeps it a real, valid row inside the same table rather than an absolutely positioned overlay that would need its own sizing logic to line up with the table underneath it.
load() is deliberately written so the very first call always fails — attempt === 1 short-circuits the random check — specifically so the error state and its Retry button are actually reachable the moment this preview loads, instead of only existing in the code and needing a lucky dice roll to ever be seen. Every retry after that has a genuine 60% chance of succeeding, closer to how a real flaky network request behaves.
Retry calls the exact same load() function the initial page load calls, which is what keeps the loading state, the error state, and the eventual data state all synchronized through one code path — there's no separate "retry logic" that could drift from what a fresh load actually does, the same one-function-owns-every-outcome pattern used in bootstrap-form-autosave-status.
Step by step
How to Use
- 1Load the snippetA brief "Loading orders..." row appears inside the table.
- 2Wait about a secondThe load intentionally fails the first time, showing a warning icon, message, and a Retry button in place of the rows.
- 3Click "Retry"The loading row reappears, and this time there's a real 60% chance the three sample orders load successfully.
- 4If it fails againClick Retry as many times as needed — each attempt runs through the identical load() logic.
- 5Once it succeedsThe real order rows replace the error message, and the column headers above never moved the whole time.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
So the error state is guaranteed to be visible the moment this preview loads, rather than depending on a random chance a viewer might never see. A real implementation should remove that special case and let the actual request outcome decide every time.
Keeping the thead in place means the user never loses the context of what columns to expect, whether the table is loading, erroring, or showing data — replacing the entire table would force a flash of missing structure on every state change.
A real table row participates in the table's own layout and sizing automatically, so the error message always matches the table's actual width with zero extra positioning logic, and it disappears cleanly the moment real rows replace it.
Yes. Track a status value (loading, error, success) in component state, and conditionally render the loading row, the colspan error row, or the mapped data rows from that single status — the load()/retry function shape carries over directly.
Replace the setTimeout and random check in load() with a real fetch call, treating a resolved, successful response as the data path and a rejected promise or non-2xx response as the error path — the render functions around it need no changes.