Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Skeleton Loading Sidebar Block for Radix UI

A metrics app in a fixed-height window whose sidebar shows its loading state: a switch holds skeleton rows of fixed widths, Reload menu runs one bounded load, and the rows are exactly as tall as the real ones so nothing moves.

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 menu loading without a jump with this skeleton loading sidebar for React with Radix UI. A window framed by a hairline with a plus mark on each corner holds a Sidebar of ten pages in three groups, Metrics, Explore and Admin, and two controls at the top of the rail: a Switch named Loading that holds the menu in its loading state, and a Reload menu Button that runs one load of 1.4 seconds and then shows the pages. While loading, every row is a Skeleton icon and bar of a width typed for that row, drawn in the same box as the real row, so the menu is the same height loading and loaded, in every style. The menu is marked aria-busy while it loads, one polite line says when it has loaded, and the shimmer stops under reduced motion. Folded to icons, the rail keeps its icons; on a phone it is a sheet. The page is the chosen page's three rows and never turns into a skeleton. It's built from Sidebar, Skeleton, Switch, Button, Badge and Guides, with a CSS entrance only. For menus that load their pages, role-based menus and any sidebar fed by a request.

FAQ

Why doesn't the menu change size when the pages arrive?

Each group draws its real menu and its skeleton rows in one grid cell, and the unseen layer keeps its space. The skeleton rows use the Sidebar's own row size class, so they are as tall as the buttons in every style, and the cell is as tall as the taller of the two. Loading and loaded are therefore the same height by construction, not by tuning.

Why not the catalog's SidebarMenuSkeleton?

It draws a new random width every time it renders, so the server's markup and the browser's differ and the bars jump on each update. This block types a fixed width for every row, as a fraction of the row, so the same bars show every time and server and browser agree.

What does Reload menu do, and what does the switch do?

Reload menu turns loading on and sets one timer for 1.4 seconds; when it fires, the pages show and the status line says so. Nothing is fetched. The switch is the same state held by hand: on, the menu stays loading until you turn it off, and turning it off or on cancels a running reload. Pressing Reload menu while it loads says so and keeps focus, and a load still running when the block goes away ends with it.

What do screen readers get while it loads?

The menu is aria-busy, the skeleton rows are hidden from assistive technology, and the real rows are hidden and inert until they return, so nothing is read or focused that isn't there yet. A single polite line says "Loading the menu." and then "Menu loaded." Under reduced motion the bars are still, not pulsing.

What stays in the page, and how do I connect it?

Everything. The pages are typed in GROUPS, and the load is a timer in the block; the block sets persist={false} and shortcut={false} so it writes no cookie. To load a real menu, call your request in place of the timer, set loading from its start and end, and keep the skeleton rows for the same pages. The pages are links to # whose handler shows the page instead of navigating. They're SidebarMenuButtons with asChild around an a element.

How does it work on a phone?

Below the medium breakpoint the sidebar is a sheet that the menu button in the top bar opens from the start edge, the right in a right-to-left layout. The switch and Reload menu are inside it, so you watch the rows load where they are. Choosing a page closes the sheet and puts focus back on the button.