shadcn.io is not affiliated with official shadcn/ui
Loading Skeleton Notification Block for Solid
A narrow activity list that shows its loading state as the exact shape of what loads: six rows of skeleton boxes that give way to the real rows without anything moving.
Installation
Overview
Show people the shape of what is coming, with this loading state for a notification list in Solid with Ark UI. A compact header holds the title, one sentence and a Button that loads the list and reloads it. A hairline frame with a plus mark on each corner holds six rows that each draw a circle, a title line, a detail line and a time: first as Skeleton boxes, then as the real Avatar, title, detail and time. Both are rows of one fixed height with the same columns, so the boxes sit exactly where the content lands and nothing moves when it arrives. The two lists share one grid cell and the one not shown is unseen, the region is marked as busy while it waits, and the foot band says "Loaded 6 notifications." once. The skeletons keep still under reduced motion. It's built from Skeleton, Avatar, Button and Guides, with a CSS entrance only. For an inbox, an activity feed or any list that loads.
FAQ
How do I fill it with my own rows?
Edit ACTIVITY at the top of the file. Each row has an id, a who, a title, a detail, an ISO at time and two class strings, titleBar and detailBar, that give the width of its two skeleton lines so the placeholders vary like real text. The loaded rows and the skeleton rows are both drawn from that one list, so they always have the same count. Times are written in UTC and shown against AS_OF: today as a clock time, yesterday as a word, older as a weekday and date.
What stays local, and how do I make it real?
The wait is a timer of about a second and a half that the button starts; nothing is fetched, and leaving the page cancels it. In your app, start from the skeletons, set a loaded flag when your request resolves, and render your own rows in the first layer. Keep the rows' fixed height and the two-layer cell: the height holding is what stops the page jumping, whatever your data looks like.
Why is nothing shifting when the rows arrive?
Every row is a grid of the same three columns (a circle, a text column, a time) at a fixed height, and each line of text sits in a box as tall as its skeleton bar's box. The bars are smaller than the boxes, never larger, and the real text is truncated to one line, so a long title can't wrap and grow its row. The loaded list and the skeleton list also occupy one grid cell, so the block is as tall as the taller of the two, which here is the same.
What does a screen reader hear?
The region is marked busy while the skeletons show, and the skeleton rows are hidden from assistive technology behind one short "Loading notifications" line. The polite status line in the foot says "Loading notifications…" when you press the button and "Loaded 6 notifications." when they arrive, once, rather than reading six empty rows. The button keeps its place and its focus while it waits: it is marked unavailable, not disabled, so keyboard users stay where they are.
What happens with reduced motion?
The skeleton boxes pulse by default. With the reduced motion setting on, the pulse is switched off and they stay as still, flat boxes; the swap to the real rows is already instant and has no animation. The panel's entrance is a short fade that also stops.
How does it look on a phone?
The panel fills the width. A row keeps its height and its three columns: the title and detail truncate to one line each, and the time column stays narrow. The button sits under the sentence in the header and keeps its width, so its label changing from "Load notifications" to "Loading…" never moves anything.