Chapter 07 of 12·Guides

Learned skeletons

Measure the real layout once, remember it, and paint it on every load after — no build step, nothing to regenerate.

A measured skeleton is only available once the markup it measures has rendered — which is never the case at the moment you actually need it. Give a layout a name and skelly closes that gap: it measures the real content when it appears, keeps the result, and replays it the next time that layout is loading.

ArticleCard.jsxreact
import { Skelly } from 'use-skelly/react'

// Give the layout a name and it starts learning.
function ArticleCard({ id }) {
  const { data, isLoading } = useArticle(id)

  return (
    <Skelly name="article-card" loading={isLoading}>
      <Article data={data} />
    </Skelly>
  )
}

// 1st load  — nothing learned yet, generic skeleton
// 2nd load  — the real layout, measured from your own DOM
// after an edit — re-measured on the next render, never stale

Why not snapshot at build time

Because a snapshot is a copy, and copies drift. A build-time artifact needs a headless browser, a CLI pass, and the discipline to re-run it every time markup changes — and when someone forgets, the skeleton is quietly wrong with nothing to signal it.

A learned layout is overwritten by the next successful render. Edit the component and the correction happens the first time anyone looks at it. There is no artifact to regenerate and no command to remember.

Per breakpoint

Layouts are stored per viewport bucket — 0, 480, 768, 1024, 1280, 1536 by default, overridable with breakpoints. A layout learned on a desktop is never replayed on a phone; each width learns itself the first time someone visits at that size.

Seeding the first visit

A brand-new visitor has learned nothing yet, so they get the generic skeleton. To give them the real one, export what your own browser learned, commit it, and render it from the server with <SkellySpecs> — the layouts ship in the HTML, and each browser replaces them with its own measurements as it goes.

layout.tsxreact
// In the browser (a dev route, or your e2e suite):
import { exportLearnedSpecs } from 'use-skelly'

copy(JSON.stringify(exportLearnedSpecs(), null, 2))
// -> { "article-card@1280": [ ... ], "article-card@768": [ ... ] }

// Commit it, then seed every first-time visitor from the server:
// app/layout.tsx
import { SkellySpecs } from 'use-skelly/react'
import specs from './skelly-specs.json'

export default function RootLayout({ children }) {
  return (
    <html><body>
      <SkellySpecs specs={specs}>{children}</SkellySpecs>
    </body></html>
  )
}

Where layouts live

In localStorage under skelly:learned:v1, capped at 120 entries with the oldest evicted first. Pass storage to use a different store, or storage: nullto keep everything in memory for the page’s lifetime. Nothing leaves the browser.

Last updated July 2026Suggest an edit or report a docs issue ↗
← previousWhole-page skeletonsnext →Theming