Skeleton
Motion-aware loading placeholder for text, avatars, and content blocks.
components/ui/skeleton.tsx
import * as React from 'react'
import { cn } from '@/lib/utils'
export interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
variant?: 'text' | 'circle' | 'rect'
loader?: 'pulse' | 'stripe' | 'shimmer'
/** Render as a one-line loading skeleton with icon block + text lines */
withHeader?: boolean
className?: string
}
export const Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(
({ className, variant = 'rect', loader = 'pulse', withHeader = false, children, ...props }, ref) => {
if (withHeader) {
return (
<div
ref={ref}
aria-hidden="true"
className={cn('flex w-full items-center gap-3 font-sans', className)}
{...props}
>
<div className="h-12 w-12 shrink-0 animate-pulse rounded-full bg-rk-canvas rk-border-sm" />
<div className="flex-1 space-y-2">
<div className="h-3.5 w-2/3 rounded-full bg-rk-canvas rk-border-sm" />
<div className="h-3.5 w-full rounded-full bg-rk-canvas rk-border-sm" />
</div>
</div>
)
}
return (
<div
ref={ref}
aria-hidden="true"
className={cn(
'relative overflow-hidden rk-border-sm',
loader === 'pulse' && 'animate-pulse bg-rk-canvas',
loader === 'stripe' && 'rk-skeleton-stripe bg-rk-primary',
loader === 'shimmer' && 'rk-skeleton-shimmer bg-rk-mint',
variant === 'text' && 'h-4 rounded-full',
variant === 'circle' && 'aspect-square rounded-full',
variant === 'rect' && 'rounded-2xl',
className
)}
{...props}
/>
)
}
)
Skeleton.displayName = 'Skeleton'
No selection tags yet — empty means no evidence, not lack of support.
DependenciesBundle weight · light
clsxtailwind-merge