Skip to Content
PatternsFeedbackSkeleton Loader

Skeleton Loader

Placeholder loading animations for various UI elements. Built using Box, Column, and Row components with CSS animations.

Text skeleton

Source

Add this CSS to your stylesheet:

@keyframes skeleton-shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } } .skeleton { background: linear-gradient( 90deg, var(--gray-300) 0%, var(--gray-200) 50%, var(--gray-300) 100% ); background-size: 200% 100%; animation: skeleton-shimmer 1.5s ease-in-out infinite; } [data-theme="dark"] .skeleton, .dark .skeleton { background: linear-gradient( 90deg, var(--gray-600) 0%, var(--gray-500) 50%, var(--gray-600) 100% ); background-size: 200% 100%; }
'use client'; import { Box, Column, Row } from '@umami/react-zen'; export function Skeleton({ width = '100%', height = '1rem', borderRadius = 'md', ...props }) { return ( <Box width={width} height={height} borderRadius={borderRadius} className="skeleton" {...props} /> ); } export function SkeletonText({ lines = 3, lastLineWidth = '60%' }) { return ( <Column gap="2"> {Array.from({ length: lines }).map((_, i) => ( <Skeleton key={i} height="0.875rem" width={i === lines - 1 ? lastLineWidth : '100%'} /> ))} </Column> ); } export function SkeletonAvatar({ size = 'md' }) { const sizeMap = { sm: '2rem', md: '2.5rem', lg: '3rem' }; return <Skeleton width={sizeMap[size]} height={sizeMap[size]} borderRadius="full" />; } export function SkeletonListItem() { return ( <Row padding="3" alignItems="center" gap="3" borderColor="muted" border="bottom"> <SkeletonAvatar /> <Column gap="2" flexGrow="1"> <Skeleton height="1rem" width="40%" /> <Skeleton height="0.75rem" width="60%" /> </Column> <Skeleton width="4rem" height="1.5rem" borderRadius="full" /> </Row> ); } export function SkeletonTable({ rows = 5, columns = 4 }) { return ( <Column> <Row padding="3" gap="4" borderColor="muted" border="bottom"> {Array.from({ length: columns }).map((_, i) => ( <Skeleton key={i} height="0.75rem" width="6rem" /> ))} </Row> {Array.from({ length: rows }).map((_, rowIndex) => ( <Row key={rowIndex} padding="3" gap="4" borderColor="muted" border="bottom"> {Array.from({ length: columns }).map((_, colIndex) => ( <Skeleton key={colIndex} height="1rem" width={colIndex === 0 ? '8rem' : '6rem'} /> ))} </Row> ))} </Column> ); }

Variations

Avatar skeleton

List item skeleton

Table skeleton

Custom skeleton