# use-skelly > Skeleton loaders measured from your real DOM at runtime and remembered, so the next > load paints your actual layout instead of a guess. No build step, no headless browser, > no generated JSON to keep in sync. Works with React, Next.js, Vue, Svelte and vanilla JS. The package is `use-skelly` on npm. MIT licensed. The core is zero-dependency. ## What makes it different Most auto-skeleton tools snapshot your DOM at build time with a headless browser and commit the result. That artifact is silently wrong the moment someone edits a component and forgets to regenerate it. skelly measures at runtime instead: give a layout a `name` and the real markup is measured once it renders, stored per viewport breakpoint, and replayed on later loads — including before that component has ever mounted. Every successful render overwrites what was stored, so a learned layout cannot go stale. ## Install ```bash npm i use-skelly ``` Import the stylesheet once at the app root: ```ts import "use-skelly/style.css"; ``` ## Core usage ```tsx import { Skelly } from "use-skelly/react"; // Wraps the real content. Children always render; skelly measures them and paints a // skeleton over the top while loading. There is no second copy of the layout. ``` A Next.js `loading.tsx` or a Suspense fallback renders *instead of* the page, so there is no DOM to measure. Use a preset there — presets are server rendered, so they land in the HTML and paint before any JavaScript runs: ```tsx export default function Loading() { return ; } ``` ## Options - `name`: stable identity for a layout. Enables learning; stored per breakpoint. - `loading`: defaults to true, so `` works as a standalone fallback. - `visual`: "shimmer" (default) | "pulse" | "optimistic" | "static" - `preset`: "dashboard" | "article" | "feed" | "profile" | "generic" - `spec`: an explicit SkellySpec[] instead of measuring - `structure`: "leaves" (default) | "surface" — whether a card with content inside emits nothing of its own, or stays as a flat backing plate behind its children - `media`: "block" (default) | "dominant-color" | "blurhash" - `rows`: number of text rows in the generic skeleton - `radius`: override every item's border radius - `cache`: false to re-measure on every mount - `breakpoints`: viewport buckets for learned layouts (default 0/480/768/1024/1280/1536) - `storage`: where learned layouts persist; null keeps them in memory only ## Exports From `use-skelly`: `skelly(element, options)` returns a release function · `measureLayout(container, options)` · `learnLayout(element, options)` · `recallSpec(name, options)` · `exportLearnedSpecs()` · `importLearnedSpecs(specs)` · `clearLearnedSpecs()` · `clearSpecCache()` · `breakpointFor(width)` · `learnedKey(name)` · `resolveStaticSpec(options, hasContent)` · `specExtent(specs)` · `compileItemProps(item, options)` · `PRESETS` · `PRESET_NAMES` From `use-skelly/react`: `Skelly`, `useSkelly`, `SkellySpecs`, `SkellySuspense` From `use-skelly/vue`: `Skelly`, `vSkelly` From `use-skelly/svelte`: `skelly` (action), `Skelly` From `use-skelly/next`: `withSkelly` From `use-skelly/build`: `snapshot(route, options)` ## Attributes - `data-skelly-ignore`: never measure this element. Decoration is skipped automatically — anything blurred, and aria-hidden elements lifted out of flow with no text of their own — but use this for whatever the heuristics miss. ## Theming CSS custom properties: `--skelly-base`, `--skelly-highlight`, `--skelly-radius`, `--skelly-speed`, `--skelly-surface`, `--skelly-surface-border`, `--skelly-optimistic`. ## Gotchas - A measured skeleton needs the markup to exist. In a route fallback it does not, so use a preset, an explicit spec, or a learned layout. - Learned layouts live in the browser, so a first-time visitor has none. Export them with `exportLearnedSpecs()`, commit the JSON, and render `` in your root layout to seed them from the server. - There is no CLI build step for measurement and no config file. Do not add one. ## Getting started - [Installation](https://useskelly.dev/docs/installation): Install the core from npm — 2.1 kB gzip, zero dependencies — and optionally a framework adapter. - [Quick start](https://useskelly.dev/docs/quick-start): One wrapper component is the entire integration. No skeleton components to write, ever. - [How it works](https://useskelly.dev/docs/how-it-works): Measure, compile, render — skelly derives skeletons from your real layout, so they can never drift. ## Guides - [Visuals](https://useskelly.dev/docs/visuals): Three loading treatments, switchable per component or set globally. All accessible by default. - [SSR & streaming](https://useskelly.dev/docs/ssr-and-streaming): Skeletons in the first byte of server HTML — before hydration, before JavaScript. - [Whole-page skeletons](https://useskelly.dev/docs/whole-page-skeletons): Snapshot entire routes at build time for instant full-page loading states on navigation. - [Learned skeletons](https://useskelly.dev/docs/learned-skeletons): Measure the real layout once, remember it, and paint it on every load after — no build step, nothing to regenerate. - [Theming](https://useskelly.dev/docs/theming): A handful of CSS custom properties match every skeleton to your design system. - [Generic presets](https://useskelly.dev/docs/presets): Scaffold page layouts and preview skeletons before writing your visual components. ## Reference - [API](https://useskelly.dev/docs/api): The complete surface: one function, a handful of options, one build-time helper. - [Skelly CLI](https://useskelly.dev/docs/cli): Scaffold fresh templates or initialize Skelly within existing codebases with a single command. - [Framework adapters](https://useskelly.dev/docs/framework-adapters): Thin idiomatic wrappers over the vanilla core, each around 0.4 kB. ## Prompt for coding agents Add use-skelly to this project and use it for every loading state. 1. Install it: npm i use-skelly 2. Import the stylesheet once, at the app root: import "use-skelly/style.css" 3. Find every hand-written skeleton, shimmer or placeholder component, and every `isLoading ? : ` branch. 4. Replace each one with a wrapper around the REAL content: import { Skelly } from "use-skelly/react"; The children always render. Skelly measures them and paints a skeleton over the top while loading, so there is no second copy of the layout to keep in sync. Delete the skeleton components you replace. 5. Give every wrapper a stable, unique `name`. That is what makes it learn: the real layout is measured once it renders, stored per breakpoint, and replayed on later loads — including before that component has mounted. Without a name it falls back to a generic shape. 6. A Next.js `loading.tsx` or a Suspense fallback has no DOM to measure, because it renders instead of the page. Use a preset there: export default function Loading() { return ; } Presets: dashboard | article | feed | profile | generic 7. Mark purely decorative elements — background orbs, glows, gradient blobs — with `data-skelly-ignore` so they do not become skeleton shapes. Notes: - There is no build step, no CLI and no config file. Do not add one. - Other frameworks: use-skelly/vue, use-skelly/svelte, or skelly(el, options) for vanilla JS. - Docs: https://useskelly.dev/docs