Animated List
A flex column that staggers items in on first mount and pops, exits and re-flows them as children are added or removed.
Loading preview…
Installation
npx shadcn@latest add https://ui.zyeon.ai/r/animated-list.jsonPrompt
The prompt behind this component — paste it into your AI assistant to recreate or adapt it.
Build a React + TypeScript "AnimatedList" component using the `motion` library
(AnimatePresence + motion.div).
Contract
- Export a forwardRef<HTMLDivElement, AnimatedListProps> where:
children: ReactNode required — each child is one list item and must carry
a stable `key` (the caller owns identity, this
component never generates one)
stagger?: number ms of entrance delay multiplied by index on first
mount only (default 80)
className?: string
- No other props — this is a pure animation wrapper, it has no opinion about
what a "list item" looks like.
Behavior
- Extract items via Children.map + isValidElement, wrap each in a motion.div
keyed by the child's own key.
- Track whether the component has completed its first mount in a ref (set
inside a useEffect, not during render). While that ref is still false,
each item's entrance transition gets `delay = index * stagger` — this is
the one-time staggered reveal. Once the ref flips true, every item added
afterwards gets delay 0 and pops in immediately, no matter its index.
- Entrance: initial opacity 0 / y +12 / scale 0.97 → animate to opacity 1 /
y 0 / scale 1 (spring transition).
- Exit: the reverse of the entrance target (opacity 0 / y +12 / scale 0.97),
driven by AnimatePresence so removed items play their own exit instead of
vanishing instantly.
- Use AnimatePresence mode="popLayout" so an exiting item is pulled out of
layout flow immediately — the remaining items animate into their new
position (via the `layout` prop) at the same time the removed item is
still fading out, not after it finishes.
- prefers-reduced-motion (motion's useReducedMotion hook): every item mounts
directly at its final state (initial={false}), transitions collapse to
duration 0, and `layout` is disabled — items appear/disappear instantly
with no animated re-flow, but add/remove still works.
Rendering & styling
- The container itself carries only "flex flex-col gap-2" — no color, no
border, no radius. It has zero visual opinion; className merges in via
cn() so the consumer's spacing/width wins.
- No semantic tokens are needed inside this component since it renders no
chrome of its own — all token usage belongs to whatever the caller passes
as children.
Customization levers
- Direction & distance: swap the y offset for x, or change 12/0.97 to make
the entrance travel further or scale more dramatically.
- Spring feel: stiffness/damping on the transition — lower stiffness or
higher damping reads calmer, higher stiffness reads snappier.
- Stagger: the `stagger` prop controls only the first-mount reveal; tune it
down for dense lists (long staggers on 20 items feel sluggish) or up for a
hero-moment reveal of 3-4 items.
- First-play trigger: pair with use-intersection-observer to delay the
initial stagger until the list scrolls into view, instead of firing on
mount.
- Layout: drop the `layout` prop if items never resize/reflow and you want
to save the extra FLIP measurement pass.Concepts
- First-play stagger, not a global stagger — the entrance delay only applies while a ref tracking "have we mounted yet" is still false; it's read once per item at render time, so items added a second later never inherit the original index-based delay.
- AnimatePresence popLayout — without it, a removed item stays in normal flow until its exit animation finishes, so the rest of the list only shifts up after the gap already closed.
popLayoutremoves it from flow immediately so the exit and the re-flow read as one motion. - Layout re-flow — the
layoutprop on each item is what makes the remaining rows glide into their new position instead of snapping there the instant a sibling is removed or inserted. - Content-agnostic wrapper — the component owns no visual styling and imports no data shape; it only orchestrates enter/exit/reflow around whatever
childrenthe caller passes, each keyed by the caller. - Reduced-motion is a real mode, not a skip — under
prefers-reduced-motion, items still mount and unmount correctly (state stays truthful), they just do so instantly instead of animating.
Sortable List
A drag-to-reorder list for settings, playlists, or priority queues, built on dnd-kit with a fully keyboard-operable fallback.
Code Block
A self-contained, copyable code panel with its own regex syntax highlighter — line numbers, line highlighting, tabs and a wrap toggle, zero external highlighting engine.