Marble UI
← Components

Skeleton

Preview: Shimmer

Loading placeholder. Hidden from assistive tech; announce loading on the container instead (aria-busy).

Import

import { Skeleton } from '@marbleui/ui'

Props

<Skeleton>

PropTypeDefaultDescription
animation'none' | 'shimmer' | 'pulse''shimmer'
shape'circle' | 'rect' | 'text''rect'

Also accepts HTML element props (278).

Usage notes

Placeholder shaped like content while it loads. For a short wait with no known layout use Spinner; for measurable progress use ProgressBar.

<div role="status" aria-busy="true" aria-label="Loading profile">
  <Skeleton shape="circle" className="size-10" />
  <Skeleton shape="text" className="w-1/2" />
</div>
  • Skeleton is aria-hidden. Announce loading on the container, with aria-busy as above, not on each piece.
  • Size it with className: shape is rect (default), circle or text (fixed 14 px height).
  • animation is shimmer (default), pulse or none; both animations are turned off under prefers-reduced-motion.
  • Match the real layout’s dimensions to avoid layout shift when content arrives.
Example source · show
import { Skeleton } from './skeleton'

const Profile = ({ animation }: { animation: 'shimmer' | 'pulse' }) => (
  <div role="status" aria-busy="true" aria-label="Loading profile" className="flex w-80 flex-col gap-4 rounded-xl border border-border bg-surface p-5">
    <div className="flex items-center gap-3">
      <Skeleton animation={animation} shape="circle" className="size-10" />
      <div className="flex flex-1 flex-col gap-2">
        <Skeleton animation={animation} shape="text" className="w-1/2" />
        <Skeleton animation={animation} shape="text" className="w-1/3" />
      </div>
    </div>
    <Skeleton animation={animation} className="h-32" />
    <div className="flex flex-col gap-2">
      <Skeleton animation={animation} shape="text" />
      <Skeleton animation={animation} shape="text" className="w-4/5" />
    </div>
  </div>
)

export const styles = {
  Shimmer: () => <Profile animation="shimmer" />,
  Pulse: () => <Profile animation="pulse" />,
  List: () => (
    <div role="status" aria-busy="true" aria-label="Loading list" className="flex w-80 flex-col divide-y divide-border rounded-xl border border-border bg-surface">
      {Array.from({ length: 4 }, (_, i) => (
        <div key={i} className="flex items-center gap-3 p-3">
          <Skeleton shape="circle" className="size-8" />
          <Skeleton shape="text" className="flex-1" />
          <Skeleton className="h-5 w-12 rounded-full" />
        </div>
      ))}
    </div>
  ),
}

Source: packages/ui/src/skeleton/skeleton.tsx

Catalog

Category
feedback
Priority
Foundation
Primitive
custom