Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Chat Message Skeleton Block for Vue

A direct-messages thread excerpt that loads in place: bars become six bubbles with initials avatars, names and times, arriving one after another from a Load press.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Show a few chat bubbles while a thread loads, with this chat message skeleton for Vue with Ark UI. A hairline frame with a plus mark on each corner holds a log of six messages from Dobby, a direct-messages app: no top bar and no composer, so it is a widget rather than a window. The "Today" label is real from the first paint; each message is a bubble whose text, sender name, initials avatar and time are Skeleton bars that become the thread in the same boxes, each message arriving a moment after the one before it when you press Load. Messages from the same person within five minutes group into a run with one name, one avatar and one time. It's built from Skeleton, Message, Bubble, Avatar, Button and Guides, with a CSS entrance only. For previews of a thread, notification cards and any bubbles that wait for a query.

FAQ

What stays local, and how do I wire it to a real thread?

Nothing is fetched. The six messages are typed in THREAD, Load starts a 700 millisecond timer, and when it ends the phase becomes ready and the data swaps in; Reload goes back to the bars and in again. To wire it for real, delete the timer, the Load button and the polite line, and set data-state (loading or ready) and aria-busy on the region from your query's pending state, or use the bars as a Suspense fallback and render the real messages when they arrive. The bars are derived from the same data as the content, so keep one component for both.

Does the thread keep its height while it loads?

Each bar and its content sit in one grid cell, so the larger of the two sets the cell's box and the swap changes nothing around it. Every bubble has the same shape, side and run position in both phases, because the bars are drawn from the same data as the messages. The chrome is real in both phases and the polite line reserves two lines, so the log is exactly as tall before, during and after loading at every width.

How are messages grouped into runs?

A message joins the one before it when both are from the same person and are at most five minutes apart. Only the first of a run shows the name, only the last shows the avatar and the time, and only the last bubble gets the squared tail corner. Change RUN_GAP to group more or less.

What do Delivered and Sent mean here?

They mark your newest message only, with a check or a double check and the word, never a color: Delivered once the other person has written after it, Sent otherwise. Both are derived from the order of the messages.

Is it accessible?

The region is marked busy while it loads and carries a screen-reader-only "Loading messages…", and the polite line announces what loaded once. The bars are hidden from assistive technology and the layer that isn't showing is invisible, so it is neither read nor focusable. The pulse stops when the data is in, and under reduced motion there is no pulse, no spinner and no stagger: the swap is instant. Each bubble starts with a screen-reader-only name and time, and the log is a named region.