## Skeleton Swap — Async Skeleton to content with zero layout shift. Docs: https://www.interior.dev/docs/skeleton-swap Reference: https://www.interior.dev/reference/skeleton-swap License: https://github.com/ddoemonn/interior/blob/main/LICENSE (MIT) ### Install Requires a React project. Install `motion`; the styled example uses Tailwind CSS utilities. The source file is copied into your project. `bunx shadcn@latest add https://www.interior.dev/r/skeleton-swap.json` Or run `bun add motion` and copy the source below. ### Usage ```tsx "use client"; import { useEffect, useState } from "react"; import { SkeletonSwap } from "@/components/interior/skeleton-swap"; export function ProfileBio({ userId }: { userId: string }) { const [bio, setBio] = useState(null); useEffect(() => { let alive = true; fetch(`/api/users/${userId}`) .then((r) => r.json()) .then((u) => alive && setBio(u.bio)); return () => { alive = false; }; }, [userId]); return (

Priya Raman

{bio ? (

{bio}

) : null}
Member since 2019
); } ``` ### Props - `ready` (`boolean`) Flips to true when the real content is available. Everything else is timing around this one value. - `children` (`React.ReactNode`) The real content. Mount it only once the data exists; the component holds the box either way. - `lines` (`number`) — default: `3` How many text lines the skeleton draws, and how much height it reserves. - `lineHeight` (`number`) — default: `21` Pixel line height of the content this stands in for. Match the text's own leading and the reserve is exact. - `barHeight` (`number`) — default: `9` Height of the bar drawn inside each reserved line. Purely visual; it does not change the reserve. - `reserve` (`number`) — default: `lines * lineHeight` Explicit box height in pixels. Use it when the content is not text: media, a chart, a fixed card. - `delay` (`number`) — default: `120` Milliseconds of silence before the skeleton is allowed to paint. A response faster than this never shows one. - `minVisible` (`number`) — default: `380` Once painted, the skeleton stays at least this long, so it cannot blink. - `label` (`string`) — default: `undefined` Names the region and announces "