Skeleton
Placeholder loading animations that mimic content layout to reduce perceived loading time.
Playground
<Skeleton
count="1"
height=""
width=""
/>01 Examples
Text Placeholder
count prop — the most common pattern for paragraphs and copy blocks.<Skeleton variant="text" count={4} />Profile Card
<Card padding="lg" class="w-full max-w-xs">
<div class="mb-4 flex items-center gap-3">
<Skeleton variant="circular" size="lg" />
<div class="flex flex-1 flex-col gap-2">
<Skeleton variant="text" size="sm" class="w-3/4" />
<Skeleton variant="text" size="xs" class="w-1/2" />
</div>
</div>
<Skeleton variant="text" count={3} />
<div class="mt-4">
<Skeleton variant="rounded" width="96px" height="32px" />
</div>
</Card>Table Rows
<div
class="border-border-subtle bg-surface-elevated w-full overflow-hidden rounded-xl border"
>
{#each [0, 1, 2, 3] as _, i (i)}
<div
class="border-border-subtle grid grid-cols-[1fr_1fr_120px_80px] items-center gap-4 px-4 py-3 [&:not(:last-child)]:border-b"
>
<Skeleton variant="text" size="sm" class="w-3/4" />
<Skeleton variant="text" size="sm" class="w-5/6" />
<Skeleton variant="rounded" width="80px" height="20px" />
<Skeleton variant="text" size="sm" class="w-12" />
</div>
{/each}
</div>Content Feed
{#each [0, 1, 2] as _, i (i)}
<div class="flex gap-4">
<Skeleton variant="rounded" class="h-20 w-20 shrink-0" />
<div class="flex flex-1 flex-col gap-2">
<Skeleton variant="text" size="sm" class="w-5/6" />
<Skeleton variant="text" size="xs" class="w-full" />
<Skeleton variant="text" size="xs" class="w-2/3" />
</div>
</div>
{/each}02 Customization
Slot Overrides
<Skeleton
variant="rectangular"
size="sm"
slotClasses={{ base: 'bg-primary/10 rounded-2xl' }}
/>
<Skeleton
variant="text"
count={3}
slotClasses={{
base: 'bg-success/10 rounded-full',
wrapper: 'gap-3'
}}
/>Fully Custom (unstyled)
<div class="flex items-center gap-3">
<Skeleton
unstyled
class="h-12 w-12 shrink-0 animate-pulse rounded-full bg-white/5 ring-1 ring-white/10"
/>
<div class="flex flex-1 flex-col gap-2">
<Skeleton
unstyled
class="h-4 w-3/4 animate-pulse rounded-full bg-linear-to-r from-white/10 via-white/5 to-white/10"
/>
<Skeleton
unstyled
class="h-3 w-1/2 animate-pulse rounded-full bg-linear-to-r from-white/10 via-white/5 to-white/10 [animation-delay:150ms]"
/>
</div>
</div>
<Skeleton
unstyled
class="h-32 w-full animate-pulse rounded-xl bg-white/5 ring-1 ring-white/10 [animation-delay:300ms]"
/>Branded placeholder shimmer repeats by design — register the look once under presets.Skeleton on BlocksProvider and apply it with preset. See Customization.
03 Accessibility
Screen Reader
Uses role="status" with aria-label="Loading". A visually hidden "Loading…"
text ensures screen readers announce the placeholder's purpose. When count > 1, individual items are marked aria-hidden="true" so only the wrapper is announced.
Reduced Motion
Both pulse and wave animations respect prefers-reduced-motion: reduce via Tailwind's motion-reduce: variant. The skeleton still renders as a
static colored block to indicate loading.
Semantic Role
The role="status" attribute identifies the skeleton as
a live region, allowing assistive technology to announce when loading completes and content replaces
the placeholder.
04 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
animation | SkeletonVariants['animation'] | — | Animation style. pulse fades opacity, wave sweeps a shimmer gradient,
none renders a static placeholder. All animations respect prefers-reduced-motion. | |
class | string | — | Extra classes merged onto the root element (or wrapper when count > 1). | |
count | number | — | Number of skeleton lines to render. Wraps items in a flex-column container when > 1. | |
gap | string | — | Tailwind gap class between repeated lines (e.g. "gap-2", "gap-4"). Only applies when count > 1. | |
height | string | — | Custom height (CSS value, e.g. "48px"). Overrides the size preset height. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ Skeleton: {...} }}>.
Prefer this over class overrides when the requested look falls outside the
semantic intent palette — presets keep hover/active/dark-mode logic coherent
and make the custom look reusable across the project. | |
size | SkeletonVariants['size'] | — | Physical dimensions following the Standard size scale (xs–xl). Dimensions vary per variant — text heights range from h-3 (xs) to h-6 (xl), circular from 24 px to 64 px. | |
slotClasses | Partial<Record<SkeletonSlots, string>> | — | Per-slot class overrides merged with (or replacing, when unstyled) tv() output. Slots: base | wrapper | |
unstyled | boolean | — | Strip all default tv() classes. Combine with slotClasses for full control. | |
variant | SkeletonVariants['variant'] | — | Shape preset. text is a slim bar, circular for avatars/icons,
rectangular for images/cards, rounded like rectangular with softer corners. | |
width | string | — | Custom width (CSS value, e.g. "200px" or "100%"). Overrides the size preset width. | |
...ElementAttributes inherited | HTMLAttributes | — | All standard HTML element attributes | |
...SkeletonVariants variant | VariantProps | — | Styling variants from SkeletonVariants |
05 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
SkeletonProps | interface | props | 0 | — | |
SkeletonVariants | type | variant | 1 | — | |
SkeletonSlots | type | variant | 0 | Slot names derived from the tv() config — single source of truth for slotClasses. |
06 Installation
Import
import { Skeleton } from '@urbicon-ui/blocks';