Westy Design Systemv0.2.7
OverviewComponentsExamplesStorybook

Foundations

  • Typography
  • Colors
  • Spacing
  • Radius
  • Shadows
  • Icons

Components

  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Data Table
  • Date Picker
  • Dialog
  • Direction
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Menubar
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Questionnaire
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Sonner
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toggle
  • Toggle Group
  • Tooltip

Chat

  • Attachment
  • Bubble
  • Marker
  • Message
  • Message Scroller

Components

Skeleton

Use to show a placeholder while content is loading.

Playground

—w-[160px]
—h-[16px]
—className
—className
Usage
Import Skeleton from the design system. It renders a div with the muted background, a medium radius and a pulse animation — give it the size and shape of the content it stands in for via className.
import { Skeleton } from "@workspace/ui/components/skeleton"

<Skeleton className="h-5 w-[100px] rounded-full" />
Avatar
Pair a circular skeleton with stacked line skeletons to hold the place of an avatar and its text. rounded-full overrides the default radius — classes passed in merge through cn(), so conflicting utilities win over the base ones.
<div className="flex w-fit items-center gap-4">
  <Skeleton className="size-10 shrink-0 rounded-full" />
  <div className="grid gap-2">
    <Skeleton className="h-4 w-[150px]" />
    <Skeleton className="h-4 w-[100px]" />
  </div>
</div>
Card
Mirror the layout of the loaded card: line skeletons in the header, an aspect-video block in the content. Keeping the skeleton and the real card the same size avoids a layout shift when the data arrives.
<Card className="w-full max-w-xs">
  <CardHeader>
    <Skeleton className="h-4 w-2/3" />
    <Skeleton className="h-4 w-1/2" />
  </CardHeader>
  <CardContent>
    <Skeleton className="aspect-video w-full" />
  </CardContent>
</Card>
Text
Stack line skeletons with a shorter last line to suggest a paragraph. Fractional widths scale with the container, so the block stays proportional at any size.
<div className="flex w-full max-w-xs flex-col gap-2">
  <Skeleton className="h-4 w-full" />
  <Skeleton className="h-4 w-full" />
  <Skeleton className="h-4 w-3/4" />
</div>
Form
Short skeletons stand in for labels, full-width ones for the fields, and a small block for the submit button. Match the vertical rhythm of the real form so the page doesn't jump on load.
<div className="flex w-full max-w-xs flex-col gap-7">
  <div className="flex flex-col gap-3">
    <Skeleton className="h-4 w-20" />
    <Skeleton className="h-8 w-full" />
  </div>
  <div className="flex flex-col gap-3">
    <Skeleton className="h-4 w-24" />
    <Skeleton className="h-8 w-full" />
  </div>
  <Skeleton className="h-8 w-24" />
</div>
Table
Repeat a row of skeletons to hold the place of tabular data — a flexible first column and fixed-width trailing columns read as a table without rendering one.
<div className="flex w-full max-w-sm flex-col gap-2">
  {Array.from({ length: 5 }).map((_, index) => (
    <div className="flex gap-4" key={index}>
      <Skeleton className="h-4 flex-1" />
      <Skeleton className="h-4 w-24" />
      <Skeleton className="h-4 w-20" />
    </div>
  ))}
</div>
RTL
Skeleton has no direction-sensitive styles of its own — in a right-to-left layout the surrounding flex or grid container flips the composition, and the skeletons follow.
<div dir="rtl" className="flex items-center gap-4">
  <Skeleton className="size-12 rounded-full" />
  <div className="space-y-2">
    <Skeleton className="h-4 w-[250px]" />
    <Skeleton className="h-4 w-[200px]" />
  </div>
</div>
API Reference
Skeleton has no props of its own — it renders a plain div and accepts every React.ComponentProps<"div">. Classes merge through cn(), so utilities like rounded-full or animate-none override the base animate-pulse rounded-md bg-muted. This is the entire component:
import { cn } from "@workspace/ui/lib/utils"

function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="skeleton"
      className={cn("animate-pulse rounded-md bg-muted", className)}
      {...props}
    />
  )
}

export { Skeleton }

Tokens used

2 design tokens consumed by the Skeleton component.

  • --muted

    Muted

    bg-muted
  • --radius-md

    Radius 0.8× (8px)

    rounded-md