Animated List
ui-beats/animated-listFreeComponent
The AnimatedList component streams its children in one at a time, springing each new entry into place and pushing the older ones down until they fall out of the bottom of the list.
Install it
npx shadcn@latest add https://uibeats.com/r/animated-list.jsonSource
1"use client";23import { AnimatePresence, motion, useReducedMotion } from "motion/react";4import { Children, useEffect, useState, type ReactNode } from "react";5import { cn } from "@/lib/utils";67interface AnimatedListProps {8 /** One entry per child, emitted in order. */9 children: ReactNode;10 /** Milliseconds between entries arriving. */11 delay?: number;12 /** How many entries stay on screen before the oldest is dropped. */13 max?: number;14 /** Start over once the last child has been emitted. */15 loop?: boolean;16 /** Announce arrivals to screen readers, for genuinely live content. */17 live?: boolean;18 className?: string;19}2021/**22 * A feed where entries arrive one at a time, pushing the ones already there23 * down and out of the bottom of the list.24 *25 * Only `max` entries are mounted at once, so a feed that has been running for26 * an hour costs exactly what it did in its first second. The shuffle is a27 * layout animation rather than hand-computed offsets: each surviving entry28 * animates from wherever it was to wherever the new arrival pushed it.29 *30 * Nothing here is real notification content — if you wire it to some, turn on31 * `live` so the arrivals are announced rather than appearing silently.32 */33export function AnimatedList({34 children,35 delay = 1400,36 max = 4,37 loop = true,38 live = false,39 className = "",40}: AnimatedListProps) {41 const entries = Children.toArray(children);42 const prefersReducedMotion = useReducedMotion();43 // Total emitted so far. The newest entry is always `emitted - 1`, which44 // makes it its own stable key however many laps the list has run.45 const [emitted, setEmitted] = useState(1);4647 const finished = !loop && emitted >= entries.length;4849 useEffect(() => {50 if (entries.length === 0 || finished || prefersReducedMotion) return;51 const timer = setTimeout(() => setEmitted((count) => count + 1), delay);52 return () => clearTimeout(timer);53 }, [emitted, delay, entries.length, finished, prefersReducedMotion]);5455 if (entries.length === 0) return null;5657 // Reduced motion gets the finished list, not a silent animation: the point58 // of the component is the arrival, and there is no quiet way to show it.59 const visibleEntries = prefersReducedMotion60 ? entries.slice(0, max).map((node, position) => ({ key: position, node }))61 : Array.from(62 { length: Math.min(emitted, max, entries.length) },63 (_, position) => {64 const sequence = emitted - 1 - position;65// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from ui-beats
All 34 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Animated Beamanimated-beam | ui-beats | Components | Free | 1 dep | |
| Border Beamborder-beam | ui-beats | Components | Free | 1 dep | |
| Bouncebounce | ui-beats | Components | Free | 1 dep | |
| Card Stackcard-stack | ui-beats | Components | Free | 1 dep | |
| Dockdock | ui-beats | Components | Free | 1 dep | |
| Fade Infade-in | ui-beats | Components | Free | 1 dep | |
| Fade In Unblurfade-in-unblur | ui-beats | Components | Free | 1 dep | |
| Flip Cardflip-card | ui-beats | Components | Free | 2 deps |
