Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

User List Skeleton Block for Ark UI

A membership directory that loads in place: six members with avatars, emails, roles and join dates, and a Follow button on each that marks the member and counts them.

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 member directory while its people load, then swap them in without moving anything, with this user list skeleton for React with Ark UI. A hairline frame with a plus mark on each corner holds a band heading with the member count, how many you follow and an Invite button, over six members from Lemniscate, a club-membership manager. The heading and the Invite button are real from the first paint; each avatar, name, email, role badge, join date and Follow button is a Skeleton box that becomes the data in the same cell, row by row, when you press Load. Follow is real and local: it marks the member and counts those you follow, and Reload clears the marks. Invite does nothing here and says so. The control is an outline Button, the bands are ruled by Guides, and a polite line says what loaded. On a phone the role and join date are left out. For directories, rosters and people pickers.

FAQ

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

Nothing is fetched. The six members are typed in MEMBERS, 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 rows when they arrive. Follow would send a request; on a real list, update the row from the response. The bars are derived from the same array as the data, so keep one row component for both.

Why doesn't the page jump when the data arrives?

Each bar and its data sit in one grid cell, so the larger of the two sets the cell's box and the swap changes nothing around it. Every row is a fixed height, the Follow button has a fixed width and keeps its label (the pressed state adds a check and changes the fill), and the role badge has a fixed width, so no state changes a row's size. The polite line under the panel reserves two lines, so the panel is exactly as tall before, during and after loading at every width.

What does Follow do?

It toggles aria-pressed on the member's button, fills it and adds a check, and updates the "following" count in the heading. The button's visible text is Follow and its accessible name adds the member's name. Nothing is stored, and Reload clears the marks.

Why does Invite do nothing?

Inviting needs an address, a role and a request, which isn't this block's job. The button is a real control that says it isn't wired in this demo; swap its handler for your own dialog.

Is it accessible?

The region is marked busy while it loads and carries a screen-reader-only "Loading members…", 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.