Status and content

Skeleton

Delay the placeholder, hold it long enough to read, then swap into reserved content.

Installation

pnpm dlx @mielui/svelte add skeleton

Usage

Wrap asynchronous content in SkeletonSwap and set ready when it arrives. Fast responses skip the placeholder; once shown, it stays long enough to avoid a flash. The placeholder lines sweep slowly and out of phase, and arriving content sharpens from a slight blur. Reduced motion turns both off. label names the region, and once the content arrives it is announced as Profile loaded.

<script lang="ts">
    import { SkeletonSwap } from '@mielui/svelte/components/skeleton';

    let { profile }: { profile: { bio: string } | null } = $props();
</script>

<SkeletonSwap ready={profile !== null} lines={3} label="Profile">
    {#if profile}
        <p>{profile.bio}</p>
    {/if}
</SkeletonSwap>

SkeletonSwap keeps its children mounted while loading but makes them inert until they show. Pass a skeleton snippet to replace the default lines; it is always inert and should contain no working controls. Skeleton is a single block for building your own placeholder: size it with w, h, and unit, round it with a class, and set variant="shimmer" for a moving highlight.

Examples

Reloading content

The button starts a 1.2 second load. The placeholder holds the height of the text, so nothing below it moves.

Timing

The 100 ms load finishes before the placeholder appears. The 1.5 s load shows it after 200 ms.

Shimmer

variant="shimmer" adds a moving highlight; the default stays still. Reduced motion turns the highlight off.

Rectangle

Circle

Card composition

Timing and size

SkeletonSwap waits delay milliseconds before showing the placeholder, 120 by default, so content that arrives sooner appears directly. Once shown, the placeholder stays for at least minVisible milliseconds, 380 by default.

lines sets the number of placeholder lines, 3 by default, and lineHeight the height of each text line in pixels, 21 by default; match it to your text so the swap does not shift. barHeight is the height of each bar, 9 by default. reserve holds a fixed height in pixels for the whole swap when content of a different height arrives. Line counts are rounded down and capped at 1,000; negative counts and sizes become zero.

On Skeleton, unit is the CSS unit for w and h, px unless you choose another such as rem or %.

Labels

Pass labels to translate the announcement SkeletonSwap makes when the content arrives. loaded is a function that receives label. Every key of SkeletonSwapLabels is optional; omitted keys keep the English text.

<script lang="ts">
    import { SkeletonSwap, type SkeletonSwapLabels } from '@mielui/svelte/components/skeleton';

    let { profile }: { profile: { bio: string } | null } = $props();

    const labels: SkeletonSwapLabels = {
        loaded: (label) => `${label} chargรฉ`
    };
</script>

<SkeletonSwap ready={profile !== null} label="Profil" {labels}>
    {#if profile}
        <p>{profile.bio}</p>
    {/if}
</SkeletonSwap>

API reference

Skeleton

Displays a static loading placeholder.

  • variant
    "default" | "shimmer"

    shimmer sweeps a highlight across the placeholder.

    Default 'default'

  • children
    Snippet<[]>

    Content rendered inside.

  • class
    string

    Classes added to the element.

  • w
    number

    Width, in unit.

  • h
    number

    Height, in unit.

  • unit
    "px" | "rem" | "em" | "%" | "vh" | "vw" | "vmin" | "vmax" | "ch" | "ex" | "cm" | "mm" | "in" | "pt" | "pc"

    CSS unit for w and h.

    Default 'px'

Skeleton.SkeletonSwap

  • ready Required
    boolean

    Shows the content when true and the skeleton when false.

  • children
    Snippet<[]>

    Content rendered inside.

  • skeleton
    Snippet<[]>

    Custom placeholder. Defaults to text lines.

  • lines
    number

    Number of placeholder lines.

    Default 3

  • lineHeight
    number

    Height of a text line in pixels.

    Default 21

  • barHeight
    number

    Height of each placeholder bar in pixels.

    Default 9

  • reserve
    number

    Height to hold in pixels so the swap does not shift the layout.

  • delay
    number

    Milliseconds to wait before showing the skeleton, so fast loads skip it.

    Default 120

  • minVisible
    number

    Shortest time in milliseconds the skeleton stays once shown.

    Default 380

  • label
    string

    Accessible name of the content. With it, the swap announces when the content arrives.

  • labels
    SkeletonSwapLabels

    Overrides the built-in text and accessible names. Every key is optional and English is the fallback.

  • class
    (string & (ClassValue | null))

    Classes added to the element.