Message skeleton
Placeholder for thread history while past messages load: alternating user bubbles and assistant paragraphs with varied line lengths. It renders through the real Message layout, so swapping in content causes no layout shift.
On this page
Installation
Usage
import { MessageSkeleton } from "@/components/ui/chatcn/message-skeleton"
<ConversationContent>
{history.isLoading ? <MessageSkeleton count={4} /> : messages.map(renderMessage)}
</ConversationContent>Use it only for history, where the layout is known. A reply that has not started yet gets the Thinking label, never a skeleton paragraph. Bubbles, the 68ch measure, the avatar gutter and the 24 px prose line all come from Message and MessageContent themselves, so the skeleton and the content it becomes always agree.
Nothing shows for the first 200 ms (delay), so fast loads never flash. The space is reserved from the first frame, so the skeleton fading in moves nothing either.
Examples
Avatars and compact
Match the messages that will replace it: avatar reserves the gutter, startWith picks the first author, and layout="compact" matches the dense log style.
Static and RTL
Props
| Prop | Type | Notes |
|---|---|---|
count | number | Messages to sketch. Defaults to 4 |
startWith | "user" | "assistant" | First author. Turns alternate. Defaults to user |
avatar | boolean | Reserve the avatar gutter |
layout | "auto" | "compact" | Match the Message layout |
seed | number | Changes the line lengths. Deterministic, so server and client agree |
pulse | boolean | Slow opacity pulse. Defaults to true, off under reduced motion |
delay | number | Ms before it shows. Defaults to 200 |
items | { from, lines?, chars? }[] | The thread's shape: each message's role plus its line count or text length. With chars the skeleton measures the column and draws the exact lines and bubble width |
label | string | Announced to screen readers. Defaults to "Loading earlier messages" |
MessageSkeletonItem draws a single message with from, lines, avatar, layout and seed, for custom sequences.
Accessibility
- The skeleton is one
statusregion with a visually hidden label. The shapes themselves are hidden from assistive technology. - Remove it when history arrives; the messages that replace it are read normally.
Motion
- No shimmer: the sweep is reserved for Thinking. Instead, a 2 s opacity pulse from 1 to 0.6 and back, on the kit's in-out curve.
- Under reduced motion the pulse stops and the shapes stay still. The 150 ms fade in after the delay is kept, since it is opacity only.