Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Card Grid Skeleton Block for Radix UI

A course catalogue grid that loads in place: six cards on shared hairlines whose drawn covers, titles, two-line blurbs and level tags come in over bars, tile by tile, when you press Load.

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 grid of cards while it loads, then swap the content in without moving anything, with this card grid skeleton for React with Radix UI. A hairline frame with a plus mark on each corner holds a band heading and six cards on shared hairlines, two columns from the small breakpoint and one on a phone: the autumn catalogue of Tamarisk, an evening-class marketplace. The heading and the grid are real from the first paint; each card's drawn cover, title, two-line blurb and level Badge, and the count in the heading, are Skeleton bars that become the class in the same place, tile by tile in reading order. Each cover is a different drawing (hills, a lake, a facade, a chair, a tile pattern, a mountain range), the blurbs are a Skeleton Text effect, and the bands are ruled by Guides. For catalogues, courses, products and any grid of cards.

FAQ

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

Nothing is fetched. The six classes are typed in COURSES, each with its own drawing. 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 card from your query's pending state, or use the bars as a Suspense fallback and render the real content when it arrives. Keep one component for the bars and the data so they can't drift apart.

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

Every cover is a fixed 2:1 box holding the bar and the drawing in the same place, every blurb's bars and words share one cell, and the cards sit on a gap-px grid whose cells stretch to the tallest in their row. The heading is real in both phases and the polite line reserves two lines, so the panel is exactly as tall before, during and after loading. The reveal runs in grid order with a short stagger, and with reduced motion the swap is instant.

How do I use real photographs for the covers?

Each cover is a drawn scene (Pic around the Scene component), so the block ships no image files. For photographs, replace the Scene inside Pic with <img alt="" className="size-full object-cover" src={course.cover} /> and keep the 2:1 box; the bar holds it until the image is ready. The drawings are data (kind, horizon, light, seed), so changing a number changes the picture.

How do I change the number of columns or cards?

The grid is sm:grid-cols-2 on gap-px bg-border; change it to sm:grid-cols-3 for three columns. Keep a count that fills the last row (six fills two or three columns), because an odd card leaves a gap showing the hairline colour. Add or remove objects in COURSES and the count in the heading and the polite line follow.

Is it accessible?

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