Skip to content
Sign in
Apps SDKWidget skeletonsMenu

Widget skeletonsPro

Loading skeletons for chat app widgets at their exact final size, and a loading boundary that hands over to the widget strip by strip with nothing moving.

ProThree widgets load in place and hand over strip by strip. Inspect fit draws each skeleton over its widget.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/widget-skeletons

Usage

tsx
import { WidgetLoading, WidgetSkeleton } from "@/components/ui/chatcn/pro/widget-skeletons"

<WidgetLoading
  loading={isLoading}
  skeleton={<WidgetSkeleton kind="list" rows={[1, 1, 2]} />}
>
  <NewsStack items={stories} />
</WidgetLoading>

Every text bone sits in a box of the real line height, so a 14 px title takes 20 px as a bar or as words, and the header, rows and footer use the widget's own paddings. list, document, message and media cover the common shapes; SkeletonHeader, SkeletonText, SkeletonStrip and SkeletonFooter build any other.

WidgetLoading holds the box from the first frame but shows nothing for 300 ms, so fast loads never flash. Once shown the skeleton stays at least 400 ms, then the widget mounts underneath and the skeleton lifts one strip at a time, top to bottom, so the widget fills in in reading order.

Turn on inspect while you build: the skeleton is drawn as dashed outlines over the loaded widget, with a note that says whether it fits to the pixel or how many pixels it is off.

Examples

Every kind, parts and fit checks

ProList, document, slides, message and media skeletons, one built from parts, and two fit checks.

Props

WidgetSkeleton

PropTypeNotes
kind"list" | "document" | "message" | "media"
rowsnumber | number[]List rows, or headline lines per row. Document outline entries
linesnumberOpen row summary, bubble, or title lines
open, imagenumber, booleanList: the row drawn open, with a picture
morebooleanThe N more row
quote, widenumber, booleanDocument: quoted lines, slides page
rationumberMedia image ratio. Default 16 / 9
expand, footerbooleanHeader button and footer actions
seednumberVaries bar lengths
pulsebooleanSlow opacity pulse. Default true
labelstring | nullSpoken while loading. Default Loading

WidgetLoading

PropTypeNotes
loadingbooleanThe widget mounts once it is false
skeletonReactNode
delaynumberMs before the skeleton shows. Default 300
minDurationnumberMs it stays once shown. Default 400
inspectbooleanOutlines and the fit note
onPhaseChange(phase) => voidwait, skeleton, handoff, done
labelsPartial<WidgetLoadingLabels>Loading and fit strings

Accessibility

  • The boundary is busy while loading and says Loading once in a polite status; the bones are hidden.
  • In forced colors the bones draw in GrayText so the shape survives.

Motion

  • The skeleton fades in over 150 ms after the delay and pulses in opacity, never a sweep.
  • Handover: each strip fades out in 200 ms, 45 ms after the one above, six at most in turn. Only opacity changes, so the widget underneath never moves.
  • Reduced motion: no pulse, and the skeleton fades out in one 150 ms step.