Skeleton
Your workspace has 12 active projects. Three are ready for review, and the next team check-in is on Friday.
Installation
npx @mielui/svelte add skeletonyarn dlx @mielui/svelte add skeletonbunx @mielui/svelte add skeletonUsage
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.
Your workspace has 12 active projects. Three are ready for review, and the next team check-in is on Friday.
Timing
The 100 ms load finishes before the placeholder appears. The 1.5 s load shows it after 200 ms.
Three invoices are overdue. The oldest is from Kestrel Logistics.
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"shimmersweeps a highlight across the placeholder.Default
'default'childrenSnippet<[]>Content rendered inside.
classstringClasses added to the element.
wnumberWidth, in
unit.hnumberHeight, in
unit.unit"px" | "rem" | "em" | "%" | "vh" | "vw" | "vmin" | "vmax" | "ch" | "ex" | "cm" | "mm" | "in" | "pt" | "pc"CSS unit for
wandh.Default
'px'
Skeleton.SkeletonSwap
readyRequiredbooleanShows the content when true and the skeleton when false.
childrenSnippet<[]>Content rendered inside.
skeletonSnippet<[]>Custom placeholder. Defaults to text lines.
linesnumberNumber of placeholder lines.
Default
3lineHeightnumberHeight of a text line in pixels.
Default
21barHeightnumberHeight of each placeholder bar in pixels.
Default
9reservenumberHeight to hold in pixels so the swap does not shift the layout.
delaynumberMilliseconds to wait before showing the skeleton, so fast loads skip it.
Default
120minVisiblenumberShortest time in milliseconds the skeleton stays once shown.
Default
380labelstringAccessible name of the content. With it, the swap announces when the content arrives.
labelsSkeletonSwapLabelsOverrides 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.
pnpm dlx @mielui/svelte add skeleton