Components
Skeleton
A versatile skeleton component that provides a visual placeholder for content while it is loading, enhancing user experience by indicating that data is being fetched or processed.
import { Card, CardContent, CardDescription, CardHeader, CardTitle,} from '@/components/ui/card'import { Skeleton } from '@/components/ui/skeleton'import { Typography } from '@/components/ui/typography'export default function SkeletonDemo() { return ( <section className='grid gap-4 sm:grid-cols-2'> <ul> <li className='mb-4 flex items-center gap-2'> <Skeleton variant='circular' className='size-9' /> <div className='flex flex-col gap-1'> <Typography variant='small' className='w-24'> <Skeleton variant='text' /> </Typography> <Typography variant='small' className='w-16'> <Skeleton variant='text' /> </Typography> </div> </li> {(['h1', 'h2', 'h3', 'h4', 'p'] as const).map((variant) => ( <li key={variant} className='flex items-start gap-2'> <Typography variant={variant} className='w-24'> <Skeleton variant='text' /> </Typography> <Typography variant={variant}>{variant}</Typography> </li> ))} </ul> <Card className='h-fit w-75'> <CardHeader> <CardTitle className='w-2/3'> <Skeleton variant='text' /> </CardTitle> <CardDescription className='w-3/4'> <Skeleton variant='text' /> </CardDescription> </CardHeader> <CardContent> <Skeleton variant='rectangular' className='h-40' /> </CardContent> </Card> </section> )}Installation
CLI
npx shadcn add https://ui.tiesen.id.vn/r/skeleton.jsonnpx shadcn add https://ui.tiesen.id.vn/r/skeleton.jsonpnpm dlx shadcn add https://ui.tiesen.id.vn/r/skeleton.jsonbunx --bun shadcn add https://ui.tiesen.id.vn/r/skeleton.jsonManual
Add the following CSS variables to your globals.css file:
@theme inline {
// ...
@keyframes shimmer {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
}Copy and paste the following code into your project.
import type { VariantProps } from 'class-variance-authority'import { cva } from 'class-variance-authority'import { cn } from '@/lib/utils'const skeletonVariants = cva( "relative block overflow-hidden rounded-sm before:inline-block before:content-['\\a0']", { variants: { variant: { text: 'h-auto w-full', circular: 'aspect-square rounded-full', rectangular: '', }, animation: { pulse: 'animate-pulse bg-current', shimmer: 'animate-[shimmer_2s_cubic-bezier(0.4,0,0.6,1)_infinite] bg-[linear-gradient(110deg,var(--color-secondary),35%,var(--color-border),50%,var(--color-secondary))] bg-size-[200%_100%] dark:bg-[linear-gradient(110deg,var(--color-secondary),35%,var(--color-input),50%,var(--color-secondary))]', solid: 'bg-muted', }, }, })interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof skeletonVariants> {}function Skeleton({ variant = 'rectangular', animation = 'shimmer', className, ...props}: SkeletonProps) { const Comp = { text: 'span', circular: 'div', rectangular: 'div', }[variant ?? 'rectangular'] as React.ElementType return ( <Comp data-slot='skeleton' className={cn(skeletonVariants({ variant, animation }), className)} {...props} /> )}export { Skeleton }Usage
Animation
The animation prop controls the loading effect style of the skeleton. Choose the one that best fits your UI/UX pattern.
// Moving light streak effect, perfect for modern dashboards (default)
<Skeleton animation='shimmer' />
// Gentle opacity fading in and out, ideal for soft, non-distracting loading
<Skeleton animation='pulse' />
// Static solid background block without any animation
<Skeleton animation='solid' />Shapes
The variant prop adjusts the shape and sizing strategies of the skeleton to match the content being loaded.
// Automatically adapts to text layouts with fluid width and fixed height block placeholders
<Skeleton variant='text' />
// Ideal for square or rectangular blocks like cards, images, or banners
<Skeleton variant='rectangular' />
// Creates a perfect circle layout, commonly used for avatars, profile pictures, or icon buttons
<Skeleton variant='circular' />