Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Loading Skeleton Notification Block for React

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.

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 people the shape of what is coming, with this loading state for a notification list in React with Base 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.

  • Pro

    A notification centre as a narrow panel: tabs for All, Mentions and Alerts with their own unread counts, rows you press to mark read, a mark all read for the open tab and a clear with an undo.

  • Pro

    Empty Inbox Notification

    Live and empty states

    A whole-screen empty inbox that can change: a drawn empty tray and one sentence, with muted conversations that come back when you unmute them and go away again when you mute them.

  • Pro

    A shared support inbox as an app window: a list of notifications under All, Unread and Archived views, and a reading pane with the sender, the time, the text, a context list and mark read, archive and undo.

  • Pro

    An inbox with an unread-only switch and a live count, where a row you read stays in place until you turn the switch off, and zero unread is a designed Inbox zero state.

  • Pro

    Real-Time Notification Feed

    Live and empty states

    A live status feed that doesn't jump: a typed script of six events starts when you press Start, new ones wait in a pill above the list, and pressing the pill lets them in at the top.

  • Pro

    A notification search in three bands: a search field with a slash key, type chips whose counts follow the query, and results with every match marked and an empty state that names the query.