webdevpuneet.com

Skeleton Chat Message Loader — Alternating Bubble Placeholders in HTML CSS JS

Skeleton Chat Message Loader · Loaders · Plain HTML, CSS & JS · Live preview

CategoryLoaders

Copy & paste

Source Code

<div class="sc-thread" id="scThread"></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

Alternating left/right sides
Received and sent skeleton bubbles alternate, mirroring a real thread.
Varied bubble widths
Each placeholder has its own width, reading as varied message lengths.
Avatar on received only
A circular placeholder appears next to left-aligned messages, matching common chat UI conventions.
Bubble-shaped corners
Large border-radius with one flattened corner mimics a real chat bubble tail.
Data-driven layout
A single MESSAGES array of { side, width } drives the entire thread.
Staggered shimmer
Per-row animation-delay gives the thread a gentle top-to-bottom ripple.
Dark, chat-ready container
Drops into any messaging UI's message list area.
Zero dependencies
Pure CSS keyframes plus a small JS loop — no library.

About this UI Snippet

Skeleton Chat Message Loader — Alternating Bubbles for a Messaging UI

Screenshot of the Skeleton Chat Message Loader snippet rendered live

A generic skeleton list of equal-width bars doesn't read as "a conversation is loading" — it reads as "a list is loading." This snippet is shaped specifically for chat: skeleton placeholders styled as message bubbles, alternating between left-aligned "received" messages (with a small avatar circle) and right-aligned "sent" messages (no avatar), each bubble a different width, so the loading state already looks like a conversation before any real content arrives.

Alternating sides via a data array

Rather than hard-coding rows, a small MESSAGES array holds { side, width } pairs — side is 'in' (received, avatar shown, bubble left-aligned with align-self: flex-start) or 'out' (sent, no avatar, bubble right-aligned via flex-direction: row-reverse and align-self: flex-end). Looping over this array and toggling classes/flex direction per row is what produces the genuine left/right alternation — a structural difference from a skeleton list where every row has identical alignment.

Varied bubble widths read as varied message lengths

Each entry's width (a percentage, 30–70% in the demo) sets that bubble's actual CSS width, so the skeleton bubbles are different sizes the way real chat messages are — a one-word reply next to a longer sentence. A uniform-width skeleton row, by contrast, reads as a table or list rather than conversational text of varying length, which is a big part of what makes this pattern feel chat-specific rather than generic.

Bubble-shaped corners, not bars

Each placeholder uses a large border-radius like a real chat bubble, with one corner flattened (border-bottom-left-radius on received, border-bottom-right-radius on sent) to mimic the little "tail" corner convention most chat UIs use — a detail that immediately reads as "message bubble" rather than "generic skeleton bar," even before the shimmer animation runs.

Avatar only on the received side

Only 'in' rows get a circular avatar placeholder next to the bubble, matching how most chat UIs show the other person's avatar next to their messages but omit your own avatar next to messages you sent — another small alternation that reinforces the specific chat-UI mental model rather than a generic alternating list. Pair it with a chat UI shell or chat message bubbles for the resolved content, or a typing indicator for the moment just before a reply streams in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JS6 alternating chat-bubble skeletons render inside a thread container.
  2. 2
    Observe the alternationReceived messages sit left with an avatar; sent messages sit right without one.
  3. 3
    Observe the widthsEach bubble is a different width, mimicking varied message lengths.
  4. 4
    Edit the MESSAGES arrayAdd, remove, or reorder { side, width } entries to change the thread shape.
  5. 5
    Replace with real messagesOnce data loads, swap each skeleton row for the actual message bubble.
  6. 6
    Restyle the bubblesChange the bubble colors, corner radius, or avatar size to match your chat UI.

Real-world uses

Common Use Cases

Messaging app thread loading
Show the shape of a conversation while it loads, using bubble-shaped placeholders that alternate sides inside a chat UI.
Support chat widgets
Show a loading thread before an agent history arrives, with avatar circles only beside received messages as in a real conversation.
Direct message previews
Give an inbox pane a convincing loading state, with varied bubble widths reading as messages of different lengths.
AI chat response lead-ins
Precede an answer in an AI chat interface with this shape, so the layout does not jump when real messages arrive.
Data-driven skeleton layout
Study how rows are created from a plain width list, with one flattened corner on each bubble mimicking a real chat tail.
Related: Page Transition Progress Bar
See the Page Transition Progress Bar for a related loaders pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

A MESSAGES array holds a side value ('in' or 'out') per row. The row's CSS class and alignment are set from that value: 'in' rows get align-self: flex-start and a leading avatar; 'out' rows get align-self: flex-end and flex-direction: row-reverse with no avatar. Because this comes from real per-row data rather than a fixed CSS pattern like nth-child(even), you can reorder or extend the array freely and the alignment always matches.

Only rows where side is 'in' (received messages) render an avatar element, matching the common chat-UI convention of showing the other person's avatar next to their messages while omitting your own avatar next to messages you sent. This is a conditional render based on the data, not a CSS visibility trick.

Each entry in MESSAGES carries its own width percentage, applied directly as that bubble's CSS width. Real chat messages vary enormously in length, so uniform-width skeleton bars would look like a generic list rather than a conversation. Randomizing or hand-picking varied widths per row is what sells the "chat message" read before any real text loads.

Generate the MESSAGES array from your actual expected message count (or a reasonable guess, like 5-8 rows) rather than hard-coding 6 entries, and randomize width and side if you don't know the real shape yet — Math.random() < 0.5 ? 'in' : 'out' and a random width in a sensible range (30-75%) works well as a stand-in.

Map over your MESSAGES-equivalent array (or a generated placeholder array) in the framework's templating syntax, conditionally rendering the avatar for 'in' rows and binding the width as an inline style or CSS variable on the bubble. The shimmer keyframe and bubble-shape CSS are framework-agnostic and need no changes.