Skeleton
Shows a placeholder shape while content loads.
skeleton-basic-demo.tsx
import { Skeleton } from "@/components/vip-ui/skeleton";export function SkeletonBasicDemo() { return ( <output className="flex w-full max-w-xs items-center gap-3"> <span className="sr-only">Loading profile</span> <Skeleton className="size-10 shrink-0 rounded-full" /> <div className="grid flex-1 gap-2"> <Skeleton className="h-3 w-3/4" /> <Skeleton className="h-3 w-1/2" /> </div> </output> );}Show more
Installation
CLI
Manual
The CLI adds the files, packages, theme variables, and Tailwind plugin that this component uses. Before you run it, set up your project.
npm
yarn
pnpm
bun
pnpm dlx shadcn@latest add https://vip-ui.dev/r/vip-skeleton.jsonCard
Skeleton is hidden from screen readers, so put a loading message in an output.
skeleton-demo.tsx
import { Skeleton } from "@/components/vip-ui/skeleton";export function SkeletonDemo() { return ( <output className="block w-full max-w-sm rounded-xl bg-card p-4 shadow-[var(--shadow-card)] ring-1 ring-border/70"> <span className="sr-only">Loading project details</span> <div className="flex items-center gap-3"> <Skeleton className="size-10 shrink-0 rounded-full" /> <div className="flex-1 space-y-2"> <Skeleton className="h-3 w-2/3" /> <Skeleton className="h-3 w-1/3" /> </div> </div> <Skeleton className="mt-4 h-3 w-full" /> <Skeleton className="mt-2 h-3 w-4/5" /> <Skeleton className="mt-4 h-9 w-28" /> </output> );}Show more
Loading state
Keep the same size so the layout does not move. Also install Avatar.
skeleton-loading-demo.tsx
"use client";import { useEffect, useState } from "react";import { Avatar } from "@/components/vip-ui/avatar";import { Button } from "@/components/vip-ui/button";import { Skeleton } from "@/components/vip-ui/skeleton";export function SkeletonLoadingDemo() { const [isLoading, setLoading] = useState(true); useEffect(() => { if (!isLoading) return; const timer = window.setTimeout(() => setLoading(false), 1500); return () => window.clearTimeout(timer); }, [isLoading]); return ( <div className="grid w-full max-w-xs justify-items-start gap-4"> <div className="w-full rounded-xl bg-card p-4 shadow-[var(--shadow-card)] ring-1 ring-border/70"> {isLoading ? ( <output className="flex items-center gap-3"> <span className="sr-only">Loading profile</span> <Skeleton className="size-10 shrink-0 rounded-full" /> <div className="grid flex-1"> <Skeleton className="my-1 h-3 w-24" /> <Skeleton className="my-1 h-3 w-32" /> </div> </output> ) : ( <div className="flex items-center gap-3"> <Avatar name="Amina Shah" /> <div className="grid min-w-0 flex-1 text-sm leading-5"> <p className="truncate font-medium">Amina Shah</p> <p className="truncate text-muted-foreground">Product designer</p> </div> </div> )} </div> <Button variant="secondary" size="sm" isDisabled={isLoading} onPress={() => setLoading(true)} > Reload </Button> </div> );}Show more