@shakhawat.dev/skeleton
v1.0.8
Published
Auto-generate skeleton loaders for React/Next.js by parsing the real DOM structure of your component. Point it at an element id and it clones the layout (width, height, radius, margin, padding, flex/grid, gap) into a shimmer/pulse skeleton.
Downloads
2,793
Maintainers
Readme
@shakhawat.dev/skeleton
Auto-generate skeleton loaders for React / Next.js by parsing your real component's DOM. Tag your real markup with an id once — never hand-draw a matching skeleton again.
Installation
Choose your preferred package manager.
npm
npm install "@shakhawat.dev/skeleton@latest"Bun
bun install "@shakhawat.dev/skeleton@latest"Yarn
yarn add "@shakhawat.dev/skeleton"Note
Quotes are only required in PowerShell because the package name starts with
@.
Quick start
1. Put an id on your real element (anywhere it normally renders):
function ProfileCard({ user }: { user: User }) {
return (
<div id="uid-skeleton-profile-card" className="card">
<img className="avatar" src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.bio}</p>
<button>Follow</button>
</div>
);
}That's it — no wrapper, no extra props. The package's global registry watches the page for
any element whose id starts with uid-skeleton, and the moment this one mounts, it's
measured (size, radius, margin/padding, flex/grid, gap) and cached automatically.
2. Drop <AutoSkeleton uid="..." /> wherever you need the loading placeholder:
import { AutoSkeleton } from "@shakhawat.dev/skeleton";
function ProfileCardLoader({ isLoading, user }: { isLoading: boolean; user?: User }) {
if (isLoading) return <AutoSkeleton uid="uid-skeleton-profile-card" />;
return <ProfileCard user={user!} />;
}No children, no duplicated markup — <AutoSkeleton uid="uid-skeleton-profile-card" /> finds
the cached (or, if already on the page, the live) #uid-skeleton-profile-card element and
renders a shimmer/pulse clone of its exact structure.
Because the tree is cached (in-memory + sessionStorage by default), it also works for the
very first paint of a route: once ProfileCard has rendered anywhere in the app in this
session, its skeleton is available everywhere, instantly, even before real data arrives.
How it works
- A single page-wide
MutationObserver(started automatically by<AutoSkeleton>) scans for elements whoseidmatches theuid-skeleton...convention. - The moment a matching element mounts,
buildSkeletonTreerecursively walks it, detecting each node's type (text,avatar,image,button,svg,box) and reading computed styles (size, border-radius, margin, padding, flex/grid, gap). - The tree is cached by id (memory +
sessionStorage), so it survives page reloads within the same browser tab/session. <AutoSkeleton uid="...">just reads that cache (or subscribes and waits for it) and renders it with<SkeletonRenderer>, memoized withReact.memo.
Alternative: <SkeletonBoundary> (all-in-one wrapper)
Prefer not to tag your own markup with an id? SkeletonBoundary wraps the real content,
measures it itself, and toggles between real/skeleton via a loading prop:
import { SkeletonBoundary } from "@shakhawat.dev/skeleton";
<SkeletonBoundary loading={isLoading}>
<ProfileCard user={user} />
</SkeletonBoundary>;Detection rules (heuristics)
<img>→avatarif it's ~square and circular (border-radius ≈ 50%) or has an avatar-ish class/alt/testid, otherwiseimage.<svg>→svg.<button>orrole="button"→button.- Text tags (
p,span,h1-h6,label,li,a, ...) with only text content →text, rendered as N lines based on measured height ÷ line-height, with a shorter ragged last line. - Everything else →
box(a structural container; flex/grid/gap are copied so children lay out the same way).
Next.js
Components are marked "use client" in the build output, so they work directly inside the
App Router without extra config. Nothing touches window/document outside of useEffect,
so it's safe with SSR.
License
MIT
