Draws a skeleton of what is about to load.
While data is missing, your own component is rendered from a sample with its text transparent, the browser lays it out for real, and rounded bars are drawn over it — one per wrapped line, one block per image or control. Nothing is guessed, so nothing moves when the content arrives.
The most common shape, one assign_async/3 and a sample:
<Skullet.skeleton :let={user} id="profile" async={@user} sample={@sample_user}>
<Profile.card user={user} />
</Skullet.skeleton>Wiring, in the app's assets/js/app.js and assets/css/app.css:
import { SkulletHook } from "../../deps/skullet/priv/static/skullet.js"
new LiveSocket("/live", Socket, { hooks: { Skullet: SkulletHook } })
@import "../../deps/skullet/priv/static/skullet.css";The CSS is not optional: it keeps the sample invisible before the engine runs.
Under a Content Security Policy the boot script and the theme's <style> block need the
page's nonce: nonce={@csp_nonce} on skeleton/1, provider/1 and box/1; see the
CSP section.
The detail lives in the guides:
- Usage — async assigns and plain data, attributes and slots, empty and error states, memory, images, plain pages without LiveView.
- Theming — every custom property, the
thememap, the provider. - Reveals — how
smartdecides, the built-in modes, specs of your own. - Internals — how measuring works, first paint, the engine's build.
Skullet.Boot.boot/1 covers the gap before the bundle loads, and Skullet.Memory
backs remember.
Summary
Functions
A placeholder block for what cannot be measured — a logo, a chart, a canvas.
Sets the skeleton's look for everything inside — one place instead of every skeleton.
Functions
@spec box(map()) :: Phoenix.LiveView.Rendered.t()
A placeholder block for what cannot be measured — a logo, a chart, a canvas.
Put it inside the skeleton's sample in place of the thing, sized like the thing:
<Skullet.skeleton :let={report} id="report" async={@report} sample={@sample}>
<Skullet.box width={240} height={120} />
<p>{report.title}</p>
</Skullet.skeleton>Attributes
width(:any) - any CSS length, or a number of pixels. Defaults to"60%".height(:any) - any CSS length, or a number of pixels. Defaults to"1em".radius(:any) - defaults to--skullet-radius. Defaults tonil.nonce(:string) - the CSP nonce for the<style>block this emits. Defaults tonil.
Sets the skeleton's look for everything inside — one place instead of every skeleton.
<Skullet.provider mode="dark" animation="group-shimmer" speed="fast">
…
</Skullet.provider>mode sets color-scheme on the wrapper, which is what the bars' colours follow, so
"dark" gives dark-page bars even where the page itself is light. theme, animation
and speed become the custom properties the skeletons inside inherit.
Attributes
id(:string) - DOM id for the wrapper. Defaults tonil.nonce(:string) - the CSP nonce for the<style>block this emits. Defaults tonil.mode(:string) - Defaults tonil.Must be one ofnil,"light","dark", or"system".animation(:string) - Defaults tonil.Must be one ofnil,"shimmer","group-shimmer","pulse", or"none".speed(:string) - Defaults tonil.Must be one ofnil,"slow", or"fast".theme(:map) - custom properties for everything inside. Defaults to%{}.- Global attributes are accepted.
Slots
inner_block(required)
@spec skeleton(map()) :: Phoenix.LiveView.Rendered.t()
Attributes
id(:string) (required) - DOM id for the hook; also the future shape key.async(:any) - a%Phoenix.LiveView.AsyncResult{}. Defaults tonil.data(:any) - plain data;nilmeans loading, anything else is loaded. Defaults tonil.sample(:any) (required) - stands in for the data to draw the skeleton.loading(:boolean) - forces the skeleton, even over data. Defaults tofalse.empty(:boolean) - shows theempty_messageslot in place of the content. Defaults tofalse.reveal(:string) -smart,together,sequence,sweep,instant, or a name registered withregisterReveal/2in JS. Defaults to"smart".theme(:map) - custom properties to override on this skeleton. Defaults to%{}.nonce(:string) - the CSP nonce for the<style>block this emits whenthemeis set. Defaults tonil.label(:string) - read to screen readers while loading. Defaults to"Loading".remember(:any) - opts in to remembering the data this skeleton last showed, under this scope. Defaults tonil.
Slots
error- rendered in place of the content when the async assign failed. It is your markup — the library styles none of it — so an alert, an empty state, or your own card frame all work here.skeleton- your own markup to measure instead of the sample — for a logo, a chart, a canvas. It is rendered while loading in place of the inner block.empty_message- rendered in place of the content whileemptyis true — the content itself when left out. It is your markup: the library styles none of it, so a dashed box, an illustration or a card frame all work here.inner_block(required) - receives the sample, then the real value.