import { Flex } from "@hel/ui/flex"
import { Skeleton, SkeletonGroup } from "@hel/ui/skeleton"
import { Stack } from "@hel/ui/stack"When to use it#
Use a skeleton while content is on its way and you know roughly what it will look like: a profile, a table, a card. It holds the space so the page does not jump when the real content arrives, and it tells people something is coming.
A skeleton should look like what it replaces, in the same sizes, so the change is smooth. For a short wait, or when you cannot guess the shape, use a Spinner. For a task with a known length, use a Progress bar.
If you load data, you may not need to place skeletons yourself. Async Content takes a skeleton and shows it at the right moment, without flashing on fast loads.
Import#
import {
Skeleton,
SkeletonText,
SkeletonGroup,
TableSkeleton,
ListSkeleton,
CardSkeleton,
FormSkeleton,
StatSkeleton,
} from "@hel/ui/skeleton"Usage#
Skeleton is one soft block. Give it a width and a height, or a size for both, and a shape: rounded, the default, square, pill or circle. Put several together to draw the shape of your content.
Skeletons are hidden from screen readers, because a block has no meaning. Wrap the set in a SkeletonGroup with a label that says what is loading, and it is announced once, as "Loading profile".
<SkeletonGroup label="Loading profile">
<Flex gap="lg" align="center">
<Skeleton shape="circle" size={48} />
<Stack gap="sm">
<Skeleton width={160} height={14} shape="pill" />
<Skeleton width={110} height={12} shape="pill" />
</Stack>
</Flex>
</SkeletonGroup>SkeletonText draws lines of text for you. lines is how many, size is sm, md or lg for the height of a line, and the last line is shorter, which lastLineWidth can change.
Animation#
animation is pulse, the default, which fades in and out. shimmer sweeps a soft highlight across, and none is still. Both moving styles stop for people who prefer reduced motion. A SkeletonGroup sets the animation for everything inside it.
The shimmer can be tuned. shimmerAngle is the slant of the band in degrees, 20 by default, and it mirrors in right-to-left layouts. shimmerStrength is how bright it is, from 0 to 1.
pulse
shimmer
none
import { Flex } from "@hel/ui/flex"
import { Skeleton } from "@hel/ui/skeleton"
import { Stack } from "@hel/ui/stack"Examples#
Swapping for the content#
loading turns a single skeleton into its content. When it is false, the skeleton renders its children instead, so a component can go from skeleton to content without changing its structure. For a whole region, show the skeleton while loading and the real thing after, as this example does.
import * as React from "react"
import { Avatar, AvatarFallback } from "@hel/ui/avatar"Ready-made skeletons#
For common shapes, use the presets: TableSkeleton, ListSkeleton, CardSkeleton, FormSkeleton and StatSkeleton. Each takes a label and an animation, and its own options, such as rows and columns for the table. A TableSkeleton takes the density of the table it stands for, so the rows are as tall.
TableSkeleton
ListSkeleton
CardSkeleton
FormSkeleton
StatSkeleton
import {
CardSkeleton,
FormSkeleton,Props#
Skeleton#
| Prop | Type | Default |
|---|---|---|
width / height | number | string | none |
size | number | string | none |
shape | "rounded" | "square" | "pill" | "circle" | "rounded" |
animation | "pulse" | "shimmer" | "none" | "pulse" |
shimmerAngle | number | 20 |
shimmerStrength | number | 1 |
loading | boolean | true |
A number is in pixels. A string can be any CSS length, such as "60%" or "12rem".
SkeletonGroup#
| Prop | Type | Default |
|---|---|---|
label | string | "Loading" |
animation | "pulse" | "shimmer" | "none" | none |
shimmerAngle / shimmerStrength | number | none |
SkeletonText#
| Prop | Type | Default |
|---|---|---|
lines | number | 3 |
size | "sm" | "md" | "lg" | "md" |
lastLineWidth | number | string | "60%" |
shape | "rounded" | "square" | "pill" | "circle" | "pill" |