Skeleton
Loading-state placeholder. Provides visual feedback while content is fetching and preserves the layout so the page doesn't jump when data lands.
The component is just a class-customizable pulsing block — sizes, shapes, and corner radius come from the Tailwind utility classes you pass in.
Aurora's other loading primitive, Spinner, lives in this doc too — see Skeleton or Spinner? for which one a given wait calls for.
Basic
html
<Skeleton class="h-4" />Common shapes
Aurora's Skeleton has no built-in shape variants — everything comes from utility classes.
Avatar
Button
Badge
Input
html
<Skeleton class="h-12 w-12 rounded-full" /> <!-- avatar -->
<Skeleton class="h-10 w-24 rounded-2" /> <!-- button -->
<Skeleton class="h-6 w-12 rounded-full" /> <!-- badge -->
<Skeleton class="h-10 w-32 rounded-2" /> <!-- input -->Text lines
Use full-width skeletons of varying widths for paragraph placeholders so the result reads as text rather than a solid block.
html
<Skeleton class="h-4 w-full" />
<Skeleton class="h-4 w-full" />
<Skeleton class="h-4 w-2/3" />Avatar + lines
html
<div class="flex items-start gap-3">
<Skeleton class="h-10 w-10 rounded-full shrink-0" />
<div class="flex flex-col gap-2 flex-1">
<Skeleton class="h-4 w-1/2" />
<Skeleton class="h-3 w-full" />
<Skeleton class="h-3 w-4/5" />
</div>
</div>Card placeholder
html
<div class="border border-primary rounded-2 p-4 flex flex-col gap-3">
<Skeleton class="h-32 w-full" />
<Skeleton class="h-5 w-2/3" />
<Skeleton class="h-3 w-full" />
<Skeleton class="h-3 w-3/4" />
</div>Table row
html
<div class="flex items-center gap-4">
<Skeleton class="h-8 w-8 rounded-full shrink-0" />
<Skeleton class="h-4 w-32" />
<Skeleton class="h-4 w-24" />
<Skeleton class="h-4 w-16 ml-auto" />
</div>Skeleton props
| Prop | Type | Default | Description |
|---|---|---|---|
class | string | object | array (Vue class binding) | — | Merged into the base animate-pulse rounded-2 bg-disabled w-full classes via tailwind-merge. Use this to control height, width, and corner radius. |
Default classes
Every Skeleton starts with these (override via class):
animate-pulse— the loading pulse animationrounded-2— default ~8px border radius; passrounded-fullfor circles,rounded-1for tighter, etc.bg-disabled— neutral gray backgroundw-full— fills the parent container; override withw-*utilities for narrower shapes
Spinner
A small rotating arc for waits with no layout to hold open — a value refreshing in place, a row action mid-flight, a modal body that hasn't arrived yet. It has no shape of its own the way Skeleton does: it's a fixed-size SVG whose width comes from the size prop, and nothing else about it is yours to style.
ts
import { Spinner } from '@scaler-tech/aurora/spinner'Skeleton or Spinner?
- Waiting for content that will fill the space — cards, table rows, text blocks, a whole panel — use Skeleton, so nothing jumps when the data lands.
- Waiting for an action to finish — a value recalculating, a row-level save, a short inline wait next to a label — use Spinner.
One exception: a button that's submitting needs neither. Use Button's own loading prop, which renders a spinner already matched to the button's size.
Sizes
xs
sm
md
lg
html
<Spinner size="xs" />
<Spinner size="sm" />
<Spinner size="md" />
<Spinner size="lg" />xs and sm render identically — both are 12px wide. That's not a bug in the demo above.
Inline with text
Refreshing…
html
<div class="flex items-center gap-2 paragraph-sm text-secondary">
<Spinner size="sm" /> Refreshing…
</div>Spinner props
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'xs' | 'sm' | 'md' | 'lg' | null | 'md' | Sets width only: lg → 20px, md → 16px, sm and xs → 12px (identical). null applies no width class, so the SVG falls back to its intrinsic 20px. |
That's the whole API. There's no color, speed, or label prop — the arc colors and the 0.8s rotation are baked in.
In practice
Standing in for an icon while work is in flight:
vue
<Spinner v-if="status === 'processing'" size="sm" />
<Icon v-else name="exclamation-circle" :size="16" />In a card header, where the surrounding content stays put:
vue
<Spinner v-if="isLoading" size="xs" />Gotchas
- Text-color utilities on a Spinner silently do nothing —
<Spinner class="text-secondary" />renders exactly like a bare one. The track and arc are hardcoded hex applied through inlinestyleattributes on the SVG paths, which no class can override. Several web-app call sites passtext-secondaryunder the illusion that it dims the spinner. size="xs"is not smaller thansize="sm"— both resolve tow-3. If you need something under 12px, Spinner can't give it to you.- A Spinner is always 20px tall no matter the
size; only its width shrinks. The SVG carriesheight="20", and Tailwind's preflightheight: autoreset coversimg/videoonly, notsvg. So asize="sm"spinner in a tight inline row contributes 20px of line height, not 12 — size it with the row'sitems-center, and don't assumesmmakes the row shorter. - Passing
:size="null"explicitly is not the same as omitting the prop:withDefaultsonly fills inundefined, sonulldrops every width class and the spinner renders at its intrinsic 20px — visuallylg. - Skeleton has
w-fullbaked in, so a Skeleton in a flex row stretches to fill unless you give it an explicitw-*orshrink-0.