moduix

Skeleton

A loading placeholder primitive for text, cards, media objects, and custom layouts.

API Reference

Skeleton is a moduix loading primitive and does not wrap a Base UI primitive. Use Skeleton, SkeletonRect, and SkeletonCircle for placeholders, then compose rows and columns with SkeletonRow and SkeletonColumn. Skeleton parts are marked aria-hidden because they describe loading shape, not user-readable content.

Basic

import { Skeleton, SkeletonColumn } from "moduix";export function SkeletonDemo() {  return (    <SkeletonColumn gap={10}>      <Skeleton height={18} />      <Skeleton height={18} width="86%" />      <Skeleton height={18} width="64%" />    </SkeletonColumn>  );}

Full list of component variables available for project-level overrides.

PropertyDefaultDescription
--skeleton-animationvar(--animation-pulse)Controls skeleton loading animation.
--skeleton-bgcolor-mix(in oklab, var(--color-muted-foreground) 18%, var(--color-background))Controls skeleton background color.
--skeleton-radiusvar(--radius-md)Controls default skeleton border radius.

Interactive variables scoped for docs preview without changing size scale tokens.

PropertyValueDefaultDescription
--skeleton-animationvar(--animation-pulse)Controls skeleton loading animation.
--skeleton-bgcolor-mix(in oklab, var(--color-muted-foreground) 18%, var(--color-background))Controls skeleton background color.
--skeleton-radiusvar(--radius-md)Controls default skeleton border radius.

Anatomy

Skeleton is composed from visual placeholder blocks and optional layout helpers. Use the shape parts for the visible loading state and the layout parts only when they match the spacing of the loaded UI.

SkeletonColumn
├─ Skeleton
├─ Skeleton
└─ Skeleton
<SkeletonColumn gap={10}>
  <Skeleton height={18} />
  <Skeleton height={18} width="86%" />
  <Skeleton height={18} width="64%" />
</SkeletonColumn>
PartRole
SkeletonBase placeholder block. Use width, height, radius, grow, and animated to size it.
SkeletonRectRectangular shortcut for cards, media, and panels. It defaults to a taller block height.
SkeletonCircleCircular shortcut for avatars, icons, and round media. It uses one size for width and height.
SkeletonRowHorizontal layout helper with gap, pt, pb, and mobileStack spacing controls.
SkeletonColumnVertical layout helper with gap, pt, pb, and grow spacing controls.

Skeleton does not use portal-like service layers such as portal, positioner, backdrop, or viewport. In most cases, keep the default shape styling and adjust dimensions with props. Use className and the CSS variables only when the placeholder needs to match a custom surface.

Composition

Use Skeleton directly for text lines and small custom blocks. Use SkeletonRect and SkeletonCircle when the placeholder has a clear geometric shape, then place those shapes inside SkeletonRow or SkeletonColumn to mirror the final layout.

All parts accept className and standard div props. The component exposes --skeleton-bg, --skeleton-radius, and --skeleton-animation for targeted styling; avoid broad overrides in a shared parent when a page demonstrates multiple skeleton styles.

Examples

Card

Use SkeletonRect with smaller line skeletons to reserve the shape of card content while data is loading.

import { Skeleton, SkeletonColumn, SkeletonRect } from "moduix";import styles from "./skeleton-card-demo.module.css";export function SkeletonCardDemo() {  return (    <div className={styles.card}>      <SkeletonRect height={148} radius="var(--radius-lg)" />      <SkeletonColumn gap={12}>        <Skeleton height={20} width="70%" />        <Skeleton height={14} />        <Skeleton height={14} width="82%" />      </SkeletonColumn>    </div>  );}
.card {  display: flex;  width: min(20rem, calc(100vw - var(--spacing-8)));  flex-direction: column;  gap: var(--spacing-4);}

Media Object

Combine SkeletonCircle, SkeletonColumn, and grow when mirroring avatar rows, list items, and comments.

import { Skeleton, SkeletonCircle, SkeletonColumn, SkeletonRow } from "moduix";import styles from "./skeleton-media-object-demo.module.css";export function SkeletonMediaObjectDemo() {  return (    <SkeletonRow className={styles.mediaObject} gap={12}>      <SkeletonCircle size={48} />      <SkeletonColumn grow gap={8}>        <Skeleton height={16} width="46%" />        <Skeleton height={14} />        <Skeleton height={14} width="72%" />      </SkeletonColumn>    </SkeletonRow>  );}
.mediaObject {  width: min(24rem, calc(100vw - var(--spacing-8)));}

Layout Props

Use gap, pt, pb, grow, and mobileStack on the layout primitives when the skeleton must reserve the same spacing as the loaded UI.

import { Skeleton, SkeletonColumn, SkeletonRow } from "moduix";import styles from "./skeleton-layout-props-demo.module.css";export function SkeletonLayoutPropsDemo() {  return (    <SkeletonColumn className={styles.layoutProps} gap={12} pt={4} pb={4}>      <SkeletonRow gap={12} mobileStack={false}>        <Skeleton width={72} height={48} />        <SkeletonColumn grow gap={8}>          <Skeleton height={14} width="62%" />          <Skeleton height={14} />        </SkeletonColumn>      </SkeletonRow>      <SkeletonRow gap={12} mobileStack={false}>        <Skeleton width={72} height={48} />        <SkeletonColumn grow gap={8}>          <Skeleton height={14} width="48%" />          <Skeleton height={14} />        </SkeletonColumn>      </SkeletonRow>    </SkeletonColumn>  );}
.layoutProps {  width: min(24rem, calc(100vw - var(--spacing-8)));}

Static

Pass animated={false} when motion is unnecessary or when the placeholder appears in dense repeated layouts.

import { SkeletonRect } from "moduix";export function StaticSkeletonDemo() {  return <SkeletonRect width={320} height={72} animated={false} />;}

Class Names

Pass className to the root or layout primitives when styling with CSS Modules, Tailwind CSS, or CSS-in-JS.

import { Skeleton, SkeletonColumn } from "moduix";import styles from "./custom-skeleton-demo.module.css";export function CustomSkeletonDemo() {  return (    <SkeletonColumn className={styles.customBlock} gap={10}>      <Skeleton className={styles.customSkeleton} height={18} />      <Skeleton className={styles.customSkeleton} height={18} width="78%" />      <Skeleton className={styles.customSkeleton} height={18} width="52%" />    </SkeletonColumn>  );}
.customBlock {  display: flex;  width: min(20rem, calc(100vw - var(--spacing-8)));  --skeleton-bg: var(--color-primary);  --skeleton-radius: var(--radius-full);  --skeleton-animation: none;}.customSkeleton {  opacity: 0.28;}

On this page