Skip to content
Caseroom

Components · Feedback

Skeleton

A loading placeholder with a slow sheen that stands still under reduced motion.

Loading the team

Both previews render the same markup; the HTML tab below is that markup without a framework.

Install

React
npx shadcn@latest add https://www.themes.dev/caseroom/r/skeleton.json
Astro
npx shadcn@latest add https://www.themes.dev/caseroom/r/astro/skeleton.json

Or copy the files below into your project. They need clsx, tailwind-merge, and the Caseroom theme. The HTML needs only the compiled CSS.

skeleton.tsx
import type { HTMLAttributes } from 'react';
import { cn } from '@/lib/utils';
import { skeletonClasses } from './skeleton.variants';

export type SkeletonProps = HTMLAttributes<HTMLSpanElement>;

/** A loading placeholder. Size it with classes (h-4 w-32, size-10 rounded-full); mark the region aria-busy. */
export function Skeleton({ className, ...props }: SkeletonProps) {
  return <span aria-hidden="true" className={cn(skeletonClasses, className)} {...props} />;
}
skeleton.variants.ts
/** A placeholder while content loads: a muted block with a slow sheen. With reduced motion it is a still block. Wrap them
 *  in role="status" aria-busy="true" with a visually hidden "Loading …"; the skeletons themselves are hidden. */
export const skeletonClasses =
  'block rounded-md bg-muted bg-[linear-gradient(90deg,transparent,color-mix(in_oklab,var(--background)_55%,transparent),transparent)] bg-[length:200%_100%] bg-no-repeat animate-[caseroom-sheen_1.6s_ease-in-out_infinite] motion-reduce:animate-none';

/** The keyframes, once in your CSS (the Caseroom theme has them). */
export const SHEEN_KEYFRAMES = '@keyframes caseroom-sheen { from { background-position: 150% 0 } to { background-position: -50% 0 } }';