webdevpuneet.com

Bootstrap Data Table Loading Skeleton — Free HTML CSS JS Snippet

Bootstrap Data Table Loading Skeleton · Tables · Plain HTML, CSS & JS · Live preview

CategoryTables

Copy & paste

Source Code

<div class="container py-5 d-flex justify-content-center">
  <div class="card bsskel-card">
    <div class="card-body p-3">
      <div class="d-flex justify-content-between align-items-center mb-2">
        <h6 class="fw-bold mb-0">Team members</h6>
        <button type="button" class="btn btn-sm btn-outline-secondary" id="bsskelReload">Reload data</button>
      </div>
      <table class="table table-sm align-middle mb-0">
        <thead>
          <tr><th>Name</th><th>Email</th><th>Role</th><th>Status</th></tr>
        </thead>
        <tbody id="bsskelBody"></tbody>
      </table>
    </div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Skeleton bar widths are shaped per column instead of being uniform placeholder rectangles
The shimmer animation runs entirely on background-position, with zero per-frame JavaScript
renderSkeleton() and renderData() both target the same tbody, so no other markup changes between states
The Reload button disables itself for the duration of the load, preventing overlapping fetch simulations
The row count and column count of the skeleton matches the real data, not an arbitrary placeholder shape

About this UI Snippet

Bootstrap Data Table Loading Skeleton — HTML, CSS & JavaScript

Screenshot of the Bootstrap Data Table Loading Skeleton snippet rendered live

A skeleton only works if it roughly matches the shape of what's coming — five identical gray rectangles read as generic "loading," while bars sized to each column's typical content read as "this table, loading." That's what the WIDTHS array does here: each of the four <td> bars gets a different percentage width (70%, 85%, 60%, 50%) echoing that a name is usually shorter than an email, which is usually longer than a role label.

The shimmer itself is a single CSS background-position animation on a gradient that's four times wider than the element (background-size: 400% 100%) — animating its position sweeps a lighter band across each bar continuously, entirely on the compositor thread, with no JavaScript driving the visual effect frame by frame. JavaScript's only job is swapping which markup is in #bsskelBody at all: renderSkeleton() for the loading state, renderData() for the resolved one, both writing into the exact same tbody so nothing about the surrounding table needs to change between states.

The Reload button disables itself for the duration of the simulated fetch specifically so a second click can't restart the skeleton mid-load and leave two competing timers racing to populate the same tbody — the same overlapping-request problem bootstrap-form-autosave-status solves for a save action instead of a fetch.

Step by step

How to Use

  1. 1
    Load the snippetFive shimmering skeleton rows appear immediately, each bar sized differently per column.
  2. 2
    Wait about 1.4 secondsThe skeleton rows are replaced by five real rows of names, emails, roles, and status badges.
  3. 3
    Click "Reload data"The skeleton reappears and the button disables itself until the simulated fetch resolves again.
  4. 4
    Click Reload again quicklyNothing happens while it's disabled — a second load can't start until the first one finishes.

Real-world uses

Common Use Cases

Dashboards and admin tables backed by an API
Replace the setTimeout with a real fetch call — pairs naturally with bootstrap-data-table-error-state for the failure path a real request also needs.
Any table that loads after an initial page render
Avoids a layout jump between "nothing" and "a full table" by showing the table's eventual shape immediately.
CART
Order history and account activity panels
Pairs with bootstrap-sticky-table-header-scroll for a longer real-data table that also loads asynchronously.
Learning shape-matched loading states
A direct, working comparison point against a generic spinner overlay — see how differently each communicates what's about to appear.

Got questions?

Frequently Asked Questions

A spinner (see bootstrap-loading-spinner-overlay) communicates "something is happening" with no hint of what. A skeleton shaped like the real table communicates the eventual layout immediately, which most research on perceived performance shows feels faster even at the identical actual load time.

Uniform bars read as a generic loading placeholder; bars sized like real content (a name column narrower than an email column) make the skeleton specifically resemble this table rather than any table, which is what makes the transition to real data feel seamless instead of jarring.

No — it is a pure CSS @keyframes animation on background-position over an oversized gradient. JavaScript is only responsible for swapping the skeleton markup for real data once the (simulated) load finishes.

Yes. Track a loading boolean in component state and conditionally render either a mapped array of skeleton rows or the real data rows from the same array-mapping logic — the CSS shimmer class needs no changes.

Replace the setTimeout in load() with an actual fetch, calling renderData() with the resolved response inside .then() and keeping the skeleton visible (and Reload disabled) for the full duration of the real request.