Animated List
- Source
- Magic UI ↗
- Author
- Magic UI
- Licence
- MIT
Loading…
Notifications spring in one by one at the top of a stack.
Code
"use client";
import { useEffect, useState } from "react";
import { cn } from "@/lib/utils";
import { AnimatedList } from "@/lab/magicui/ui/animated-list";
interface Item {
name: string;
description: string;
icon: string;
color: string;
time: string;
}
const base: Item[] = [
{ name: "Payment received", description: "Magic UI", time: "15m ago", icon: "💸", color: "#00C9A7" },
{ name: "User signed up", description: "Magic UI", time: "10m ago", icon: "👤", color: "#FFB800" },
{ name: "New message", description: "Magic UI", time: "5m ago", icon: "💬", color: "#FF3D71" },
{ name: "New event", description: "Magic UI", time: "2m ago", icon: "🗞️", color: "#1E86FF" },
];
const notifications = Array.from({ length: 10 }, () => base).flat();
const Notification = ({ name, description, icon, color, time }: Item) => {
return (
<figure
className={cn(
"relative mx-auto min-h-fit w-full max-w-[400px] cursor-pointer overflow-hidden rounded-2xl p-3",
"transition-all duration-200 ease-in-out hover:scale-[103%]",
"bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]",
"transform-gpu dark:bg-transparent dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] dark:backdrop-blur-md dark:[border:1px_solid_rgba(255,255,255,.1)]",
)}
>
<div className="flex flex-row items-center gap-3">
<div className="flex size-10 shrink-0 items-center justify-center rounded-2xl" style={{ backgroundColor: color }}>
<span className="text-lg">{icon}</span>
</div>
<div className="flex flex-col overflow-hidden">
<figcaption className="flex flex-row items-center text-lg font-medium whitespace-pre text-neutral-950 dark:text-white">
<span className="text-sm sm:text-base">{name}</span>
<span className="mx-1">·</span>
<span className="text-xs text-gray-500">{time}</span>
</figcaption>
<p className="text-sm font-normal text-neutral-600 dark:text-white/60">{description}</p>
</div>
</div>
</figure>
);
};
export default function AnimatedListDemo() {
// The list stops after the last item; start over so the card keeps moving.
const [run, setRun] = useState(0);
useEffect(() => {
const id = setInterval(() => setRun((r) => r + 1), notifications.length * 1000 + 2000);
return () => clearInterval(id);
}, []);
return (
<div className="relative flex h-full w-full flex-col overflow-hidden p-3">
<AnimatedList key={run}>
{notifications.map((item, idx) => (
<Notification {...item} key={idx} />
))}
</AnimatedList>
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-1/4 bg-gradient-to-t from-[#fbfaf7]"></div>
</div>
);
}
Prompt
Build "Animated List", a layout animation. What it does: Notifications spring in one by one at the top of a stack. Write it as a single self-contained React component in TypeScript that fills its parent (w-full h-full), styled with Tailwind CSS, using motion. Technique: the Motion library. It sits on a light background, in a 16:10 frame. Respect prefers-reduced-motion with a still state, pause work when the element is off screen, and keep it accessible. Original: https://magicui.design/docs/components/animated-list (Magic UI, MIT).