Skip to content

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 ​

PropTypeDefaultDescription
classstring | 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 animation
  • rounded-2 — default ~8px border radius; pass rounded-full for circles, rounded-1 for tighter, etc.
  • bg-disabled — neutral gray background
  • w-full — fills the parent container; override with w-* 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 ​

PropTypeDefaultDescription
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 inline style attributes on the SVG paths, which no class can override. Several web-app call sites pass text-secondary under the illusion that it dims the spinner.
  • size="xs" is not smaller than size="sm" — both resolve to w-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 carries height="20", and Tailwind's preflight height: auto reset covers img/video only, not svg. So a size="sm" spinner in a tight inline row contributes 20px of line height, not 12 — size it with the row's items-center, and don't assume sm makes the row shorter.
  • Passing :size="null" explicitly is not the same as omitting the prop: withDefaults only fills in undefined, so null drops every width class and the spinner renders at its intrinsic 20px — visually lg.
  • Skeleton has w-full baked in, so a Skeleton in a flex row stretches to fill unless you give it an explicit w-* or shrink-0.