Skip to content
WANCRAFT

G.90Data & Feedback

Skeleton Loader

Content-aware skeletons that mirror your layout, shimmer in sync and crossfade into real content without layout shift.

NewReactCSSTailwindintermediate

Overview

Skeleton Loader wraps the real content and stands in for it while loading. In auto mode it renders the children invisibly, measures every text line and image after mount, and draws a placeholder for each — so the skeleton is your layout, not a guess. Presets cover text, avatar rows, cards and tables when there is nothing to measure. Placeholders and content share one grid cell, so the size is reserved and the swap is a calm crossfade with no layout shift. A single highlight sweeps across all shapes in sync (or a gentle pulse), runs on the Web Animations API and pauses offscreen and in background tabs. The wrapper is aria-busy with a screen-reader status, and reduced motion gets static placeholders.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/skeleton-loader.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/skeleton-loader.json

Method 02 · Copy and paste

  1. 1. No extra dependencies — React and Tailwind CSS only.
  2. 2. Copy the source from the Code tab into components/ui/skeleton-loader.tsx.
  3. 3. Import it and adjust props.
Dependencies
None
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { SkeletonLoader } from "@/components/ui/skeleton-loader";

export function SkeletonLoaderExample() {
  const loading = true; // e.g. from your data hook

  return (
    <SkeletonLoader loading={loading}>
      <div className="flex items-center gap-3">
        <img src="/avatar.png" alt="" className="size-10 rounded-full" />
        <div>
          <p className="font-medium">Aisyah Rahman</p>
          <p className="text-sm text-zinc-500">Product designer</p>
        </div>
      </div>
    </SkeletonLoader>
  );
}

03Props

PropTypeDefaultDescription
loadingboolean—Required. Show placeholders (true) or the content (false).
childrenReactNode—The real content. In auto mode it renders invisibly (inert) while loading so it can be measured.
preset"auto" | "text" | "avatar" | "card" | "table""auto"Mirror the children, or draw a fixed placeholder layout.
linesnumber3Text lines in the text and card presets.
rowsnumber4Rows in the table preset.
animation"wave" | "pulse" | "none""wave"Synchronised highlight sweep, opacity pulse, or static.
direction"ltr" | "rtl""ltr"Direction of the wave.
speednumber1600One sweep or pulse, in ms.
radiusnumber6Corner radius of rectangular placeholders in px.
fadeDurationnumber300Crossfade into the content in ms; 0 swaps instantly.
labelstring"Loading"Status text announced to screen readers while loading.
...propsHTMLAttributes<HTMLDivElement>—Forwarded to the wrapper.

04Features

  • 01Auto mode mirrors the children's text line boxes and images, measured after mount and on resize
  • 02Text, avatar, card and table presets
  • 03One synchronised wave across all shapes, or a pulse; direction and speed configurable
  • 04Placeholders and content share a grid cell: size reserved, crossfade without layout shift
  • 05Web Animations pause offscreen and in hidden tabs
  • 06Mark elements with data-skeleton="block" or "ignore" to tune auto mode
motion · continuousmotion · tweeninput · viewport

05Accessibility

  • The wrapper sets aria-busy while loading and contains a role="status" region that announces the label.
  • Placeholders are aria-hidden; hidden content is visibility: hidden and inert, so it cannot be focused or read until it appears.
  • With prefers-reduced-motion the placeholders are static (no wave or pulse) and the crossfade is removed.

06Limitations

  • Auto mode needs children that render while loading (with placeholder data or empty strings that still take space).
  • Preset layouts are approximations; if the content is larger, the wrapper grows to the content's size when it appears.