Skeleton
A placeholder loading component that mimics the shape of content while it loads. Supports box, pill, and circle shapes with configurable dimensions.
import { Skeleton } from "@tenstorrent/vesper/skeleton";
export default function SkeletonDemo() {
return (
<div
style={{
display: "flex",
alignItems: "center",
gap: "var(--vesper-spacing-4)",
}}
>
<Skeleton size={48} />
<div
style={{
display: "flex",
flexDirection: "column",
gap: "var(--vesper-spacing-2)",
}}
>
<Skeleton width={200} height={16} />
<Skeleton width={140} height={16} />
</div>
</div>
);
}Options
| Prop | Type | Description | Default |
|---|---|---|---|
shape | "box" | "pill" | "circle" | The shape of the skeleton placeholder. | "box" |
size | number | string | Sets both width and height simultaneously. Overrides individual width and height props. | — |
width | number | string | The width of the skeleton. | — |
height | number | string | The height of the skeleton. | — |
show | boolean | When true, renders the skeleton overlay. When false, renders only the children without the skeleton. | true |
children | ReactNode | Optional content rendered behind the skeleton overlay. | — |
Any additional props are forwarded to the wrapping <div> element.
Examples
Basic usage
Render a Skeleton by giving it a width and height:
import { Skeleton } from "@tenstorrent/vesper/skeleton";
export default function BasicSkeleton() {
return <Skeleton width={200} height={20} />;
}If the width and height of the Skeleton are the same, you can also use the size prop to set both the width and height at the same time:
import { Skeleton } from "@tenstorrent/vesper/skeleton";
export default function SizedSkeleton() {
return <Skeleton size={48} />;
}Skeleton shapes
A Skeleton can be rendered in one of three shapes: "box", "pill", or "circle". The default shape is "box", which is a rectangle with slightly rounded corners:
import { Skeleton } from "@tenstorrent/vesper/skeleton";
export default function BoxSkeleton() {
return <Skeleton shape="box" width={120} height={48} />;
}Pass shape="circle" to render an ellipse:
import { Skeleton } from "@tenstorrent/vesper/skeleton";
export default function CircleSkeleton() {
return <Skeleton shape="circle" size={48} />;
}Pass shape="pill" to render a rectangle with corners that round to a semicircle:
import { Skeleton } from "@tenstorrent/vesper/skeleton";
export default function PillSkeleton() {
return <Skeleton shape="pill" width={120} height={32} />;
}Rendering children
Skeleton can be rendered with content, in which case the Skeleton takes the dimensions of the bounding box of its children, so you don't need to pass a width and height:
import { Skeleton } from "@tenstorrent/vesper/skeleton";
export default function SkeletonWithChildren() {
return (
<Skeleton>
<div style={{ width: 100, height: 100 }} />
</Skeleton>
);
}Conditional rendering
You will typically be using Skeleton to conditionally render loading states for your components. The easiest way to do this is to simply render the Skeleton in place of other elements while they are loading:
import { Skeleton } from "@tenstorrent/vesper/skeleton";
import { Typography } from "@tenstorrent/vesper/typography";
export default function LoadingContent({ loading }: { loading: boolean }) {
if (loading) {
return <Skeleton width={202} height={48} />;
}
return <Typography>Content loaded!</Typography>;
}You can also use the show prop to hide/show Skeleton children conditionally. When show is set to true, the Skeleton will mask its children. When show is set to false, the children are rendered in place with no masking instead.
In the example below, we fetch data asynchronously in a useEffect callback, masking the Skeleton's children while the request is being made:
import { useEffect, useState } from "react";
import { Skeleton } from "@tenstorrent/vesper/skeleton";
import { Typography } from "@tenstorrent/vesper/typography";
export default function MaskedChildrenExample() {
const [data, setData] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
fetch("https://example.com/api/v1/data", { signal })
.then((res) => res.json())
.then((data) => setData(data))
.catch(() => {
if (!signal.aborted) setData("Failed to fetch data");
});
return () => controller.abort();
}, []);
return (
<Skeleton show={data === null}>
<Typography variant="heading-md">
{data ?? "Fallback text while data is loading"}
</Typography>
</Skeleton>
);
}Usage with Suspense
Skeleton can also be used as a Suspense fallback. This is useful for when you are reading data from a promise client-side using use, streaming data from Server Components, or lazy-loading component code with lazy:
import { Suspense, use } from "react";
import { Avatar } from "@tenstorrent/vesper/avatar";
import { Skeleton } from "@tenstorrent/vesper/skeleton";
function UserAvatar({ userId }: { userId: string }) {
const avatar = use(fetchAvatar(userId));
return <Avatar size="lg" src={avatar.url} alt={avatar.altText} />;
}
export default function UserProfile() {
return (
<Suspense fallback={<Skeleton size="3rem" />}>
<UserAvatar userId="abc123" />
</Suspense>
);
}