shadcn.io is not affiliated with official shadcn/ui
Activity Feed Skeleton Block for Radix UI
A code-hosting activity page that loads in place: two day groups of entries on a rail, with a Show menu that keeps merges, issues or pushes, swapped in from bars when you press Load.
Installation
Overview
Show an activity feed while its entries load, then swap them in without moving anything, with this activity feed skeleton for React with Radix UI. A hairline frame with a plus mark on each corner holds a band heading, a Show menu and two day groups of entries on a vertical rail, from Frieze, a code-hosting service. The heading, the menu and the rail are real from the first paint; each day label, avatar, sentence (who merged, opened, closed or pushed what) and time is a Skeleton bar that becomes the data in the same cell, the day labels first and then the entries one after another, when you press Load. The Show menu is a native select that keeps one kind of entry; an unseen copy of the whole feed holds the height so nothing moves when it filters. The control is an outline Button, the bands are ruled by Guides, and a polite line says what loaded. It's built from Skeleton, Avatar, Button, Native Select and Guides, with a CSS entrance only. For repository pages, audit trails and any timeline of who did what.
FAQ
What stays local, and how do I wire it to a real query?
Nothing is fetched. The five entries are typed in ENTRIES, 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. The Show menu filters that same array; on a real feed, send the kind to your query instead. 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. The day groups, the rail and the menu are real in both phases, and the filter is held by an unseen copy of the full feed in the same grid cell, so showing only merges leaves the panel as tall as showing everything. The polite line under the panel reserves two lines, so the panel is exactly as tall before, during and after loading at every width.
How does the Show menu work?
It is a native select with four options (all activity, merges, issues, pushes). Choosing one hides the entries of other kinds and any day with none left, and the count beside the heading and the polite line say how many of the five remain. It works while the bars are showing too, because the bars are drawn from the same filtered list.
How is the rail drawn, and does it work right to left?
Each entry draws a one pixel line from its avatar down to the next, behind the avatar, and the last entry of a day draws none. Everything uses logical sides (start-4, ps, pe), and the line's centring flips under rtl, so the rail stays under the avatars. Names sit in bdi and times in left-to-right spans.
Is it accessible?
The region is marked busy while it loads and carries a screen-reader-only "Loading activity…", 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.