The placeholder silhouette. Its radius matches the real thing it replaces: avatar/chip pill, thumb card radius, button control radius, line the track radius.
motion
'shimmer' | 'pulse'
'shimmer'
Animation style. shimmer sweeps a token highlight across the block (transform only); pulse animates opacity. Both freeze under prefers-reduced-motion.
width
number | string
n/a
Block width (px or any CSS length). Lines accept percentages; the last line of a SkeletonText is rendered shorter automatically.
size
number
n/a
Convenience for square shapes (avatar): sets both width and height.
radius
'track' | 'control' | 'card' | 'pill'
n/a
Override the shape's default radius token when composing a custom footprint.
lines
number
1
On SkeletonText: how many stacked lines to render, with a consistent gap and a shorter final line.
The skeletons are presentational (aria-hidden). Wrap the loading region in one element with aria-busy + aria-live=“polite” so assistive tech hears a single “Loading” status, not every empty box.
Parameter
Type
Default
Description
Modifier.skeleton
Modifier
n/a
The placeholder treatment: fills the modified box with a recessed surface (cardSurface1 mixed toward the canvas) and draws the travelling shimmer over it. Apply after .clip(shape) so the silhouette matches.
motion
SkeletonMotion
Shimmer
Shimmer (travelling highlight, animated via rememberInfiniteTransition on a translation) or Pulse (animated alpha). Resolves to a static placeholder when Settings.Global.ANIMATOR_DURATION_SCALE is 0.
shape
Shape
NockerlProgressTrackShape
Clip applied to the box before .skeleton(), e.g. CircleShape for an avatar, NockerlCardShape for a thumb, the 2.dp track shape for text lines.
modifier
Modifier
Modifier
Standard sizing/layout modifier on the composed row skeleton (e.g. heightIn(min = 56.dp) to clear the 48dp target).
The Swift idiom documented here is the system redaction API rather than a bespoke
type. Apply .redacted(reason: .placeholder) to the real view while data is loading;
SwiftUI greeks it into placeholder bars at the same footprint.
Style
Type
Default
Description
.redacted(reason:)
ViewModifier
n/a
Renders the view as placeholder content. Pass .placeholder for the skeleton treatment; the view keeps its exact size so the layout doesn't shift.
.unredacted()
ViewModifier
n/a
Opts a child view out of an ancestor's redaction (e.g. keep a leading icon visible while text is greeked).
redactionReasons
EnvironmentValues
n/a
Read in @Environment(\.redactionReasons) to branch behaviour while redacted, e.g. skip a network image or supply placeholder text.
.accessibilityLabel
ViewModifier
n/a
Give the redacted region a single label ("Loading…") so VoiceOver announces the loading state rather than greeked content.
Cross-platform drift (tracked for reconciliation)
Skeleton is the content-shaped loader, distinct from its three feedback
siblings: the spinner (an indeterminate circular loader with no footprint), the
progress-bar (determinate/indeterminate progress against a known total), and the
empty-state (a resolved zero-data result). Reach for a skeleton only when you
know the shape of what’s arriving and want the layout to stay put.
Designed, not transcribed. The system above is drawn originally from the
laws rather than lifted off a native screen, and the loading regions it is meant to
replace fall back to a circular indicator. Promoting it to a real core/uiModifier.skeleton() is the open work.
The Swift primitive is system redaction..redacted(reason: .placeholder)
greeks the real view, so the footprint is automatically exact, but it does not
shimmer. Android/web add the travelling highlight; Swift’s placeholder is static
unless a custom overlay is added. One shimmer expression needs to win.
Block radius. Web/Compose match each block’s radius to the component it
replaces (avatar pill, thumb card, line track); Swift’s redaction inherits the
view’s own shape. Behaviour converges; the source differs.