Skeleton Shimmer Loader
Loading-state placeholder blocks with a light sweep animating across them.
- Category
- Assets
- Added
- 2026-08-22
- Deps
- none
- Updated
- 2026-09-01
Props
"use client";
export function SkeletonShimmer({ rows = 3, avatar = true, speed = 1.6 }: {
rows?: number;
avatar?: boolean;
speed?: number;
}) {
return (
<div
role="status"
aria-busy="true"
className="flex w-full max-w-sm items-start gap-3"
style={{ "--shimmer-speed": `${speed}s` } as React.CSSProperties}
>
<span className="sr-only">Loading…</span>
{avatar && <div className="shimmer h-11 w-11 shrink-0 rounded-full bg-neutral-200" />}
<div className="flex flex-1 flex-col gap-2.5 pt-1">
{Array.from({ length: rows }).map((_, i) => (
<div
key={i}
className="shimmer h-3 rounded-full bg-neutral-200"
style={{ width: i === rows - 1 ? "60%" : "100%" }}
/>
))}
</div>
{/* speed comes in via the --shimmer-speed CSS variable set above, not
a ${speed}s template interpolation inside this style block —
interpolating directly into styled-jsx here compiles to a literal
0s animation-duration, so the block stays fully static. */}
<style jsx>{`
.shimmer { position: relative; overflow: hidden; }
.shimmer::after {
content: "";
position: absolute;
inset: 0;
transform: translateX(-100%);
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.7) 50%, transparent);
animation: shimmer-sweep var(--shimmer-speed, 1.6s) ease-in-out infinite;
}
@keyframes shimmer-sweep {
100% { transform: translateX(100%); }
}
`}</style>
</div>
);
}
About this asset
Loading placeholders are one of the few UI patterns where pure CSS genuinely beats a JS animation loop; there is no interactivity or state to track, just a sweep that has to run continuously and cheaply while real content loads. Each placeholder block shares one shimmer class whose after pseudo-element is an absolutely positioned gradient sweeping left to right on an infinite keyframe loop, and every block needs its own overflow-hidden and relative positioning so the sweep clips to that block's own rounded shape instead of one highlight sliding across the whole row as a single rectangle. The speed prop follows the same CSS-custom-property pattern used elsewhere in this library: set once via inline style as a shimmer-speed variable and read with var() inside the styled-jsx block, never interpolated directly into the template literal, which compiles to a static, non-functioning duration. The last line is intentionally rendered narrower than the rest to mimic a natural paragraph's line wrap. The whole thing is decorative markup with no semantic connection to the content it stands in for.
Curator’s note
One shared .shimmer class with overflow-hidden per block, not one giant sweep over the whole row — that's what keeps the highlight looking like it's moving across each rounded shape instead of just sliding over a rectangle.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Page Loading Bar
assetsAn nprogress-style top-of-page bar that eases toward completion, never stalling.
Async Action Button
componentsA button that survives a real round trip — a minimum pending duration, a width that never twitches, aria-disabled instead of disabled.
Spotlight Card Grid
patternsA card grid with a soft red spotlight that follows the cursor across it.