Skelly - Skeletons that draw themselves — from your own UI | Product Hunt

Skeletons that learn your UI.

skelly measures your rendered UI, then remembers it — so the next load paints a skeleton of your actual layout, not a guess. No build step, no headless browser, nothing to regenerate when your markup changes.

2.1 kB gzip·zero deps·MIT
loading — skeleton by skelly
Amara Osei
Posted 2 hours ago
Follow
Shipped the new onboarding flow today. Conversion is up 12% and the skeleton states made the whole thing feel instant — even on 3G.
cover image
♥ 248↻ 31✉ 12
← one component. skelly generated the skeleton. →
01 — wrap
<Skelly loading={isLoading}>
  <ProfileCard />
</Skelly>

Wrap anything — a button, a table, an entire page.

02 — analyze
▸ 1 avatar (circle)
▸ 3 text lines (61–100%)
▸ 1 image block

skelly measures the real layout — shapes, radii, line lengths.

03 — render

A matching skeleton renders instantly — client or server.

Every loading state. One API.

Pick a visual per component or set one globally. All of them respect prefers-reduced-motion.

Shimmer
GPU-composited sweep. The classic.
Pulse
Quieter. Great for dense tables.
"Untitled draft" ✓ saved
Optimistic
Render expected data, reconcile on arrival.
HTML → skeleton inlined
SSR / streaming
Skeletons in the first byte. No hydration flash.
Whole pages
Route-level snapshots generate full-page skeletons at build time.
Text-aware
Line count and ragged-edge widths match your real copy.
Images & media
Aspect-ratio-locked blocks. Optional dominant-color fill.
Tables & lists
Row-count hints; sticky headers stay real while cells load.

Hand it to your agent.

One paste. It installs skelly, finds the hand-written skeletons already in your codebase, replaces them, and deletes what it replaced.

prompt — paste into Claude Code, Cursor, or any agent
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 ? <Skeleton /> : <Content />` branch.

4. Replace each one with a wrapper around the REAL content:

     import { Skelly } from "use-skelly/react";

     <Skelly name="article-card" loading={isLoading}>
       <ArticleCard data={data} />
     </Skelly>

   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 <Skelly preset="dashboard" />;
     }

   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

Agent already browsing? Point it at useskelly.dev/llms.txt — the whole API in one fetch.

Your framework. Or none.

The core is vanilla — 2.1 kB, framework-agnostic. Thin adapters where you want idioms.

import { Skelly } from 'use-skelly/react'

<Skelly loading={isLoading} visual="shimmer">
  <ProfileCard user={user} />
</Skelly>

Stop hand-rolling gray boxes.

// hand-rolled skeletons
One skeleton component per real component — forever out of sync
Redesign a card, forget its skeleton, ship the layout jump
Client-only — blank page until hydration
Every dev invents their own shade of gray
// build-time snapshot tools
Needs a headless Chromium download and a CLI pass before it works
Commits massive JSON coordinate files directly into Git history
Silently stale the moment someone edits a component and forgets to rebuild
Cannot placeholder empty or undesigned page layouts
// skelly (learns at runtime)
Measures your real DOM at runtime — no browser binary, no CLI pass
Remembers what it measured, so the next load is a real skeleton
Self-healing: re-learns on every render, so it can never go stale
Learned per breakpoint, and exportable to seed first-time visitors

Small enough to not think about.

Bundle size, minified + gzip. Core only; adapters add ~0.4 kB.

skelly
2.1 kB
react-loading-x
8.9 kB
placeholder-ui
13.6 kB
hand-rolled (avg)
26+ kB

Live Performance Benchmark

Test Skelly's layout-compiling speed on your current browser CPU thread.

0 ms
layout shift (CLS) introduced
< 1 ms
analysis per component, cached
100%
GPU-composited animations

Ship the skeleton, not the spinner.

$npm i use-skelly