Skip to content

Animated List

A list whose items slide and fade in one after another, and stack as new ones are added. It is a real list (ul with li items), and every item is in the page from the start.

Motionv0.1.0experimentalWCAG 2.2 AAView spec

Optional, and kept apart.

Nothing here is included in a project unless you add it, and the plain component it builds on is unchanged. This effect is decoration: it is hidden from screen readers, stands still for visitors who prefer reduced motion, and can be paused. Use Still on a preview to see that version.

  • Payment received
  • Invoice sent to Dana
  • Stock reserved
  • Courier booked
import { AnimatedList } from "@rdloom/react";

const steps = ["Payment received", "Invoice sent to Dana", "Stock reserved", "Courier booked"];

export default function AnimatedListBasicExample() {
  return (
    <div className="flex w-full justify-center">
      <AnimatedList className="w-full max-w-sm">
        {steps.map((step) => (
          <div
            key={step}
            className="rounded-[var(--rd-radius-control)] border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-raised)] px-4 py-3 text-sm text-[var(--rd-color-text-default)]"
          >
            {step}
          </div>
        ))}
      </AnimatedList>
    </div>
  );
}

Installation

npx rdloom add animated-list

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes.

It also adds rdloom-motion.css next to your tokens and imports it from the tokens file, once: no setup, and no inline styles. The plain components you already use are untouched.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/animated-list.json

Works, but without upgrade tracking, and you import the motion CSS yourself.

Usage

import { AnimatedList } from "@rdloom/react";

<div className="flex w-full justify-center">
  <AnimatedList className="w-full max-w-sm">
    {steps.map((step) => (
      <div
        key={step}
        className="rounded-[var(--rd-radius-control)] border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-raised)] px-4 py-3 text-sm text-[var(--rd-color-text-default)]"
      >
        {step}
      </div>
    ))}
  </AnimatedList>
</div>

Live feed

  • Order 1042 shipped
  • New sign-up from Lisbon
import { useState } from "react";
import { AnimatedList, Button } from "@rdloom/react";

const events = ["New sign-up from Lisbon", "Order 1042 shipped", "Refund issued for order 1037", "Comment on the roadmap page", "Backup finished"];

export default function AnimatedListLiveFeedExample() {
  const [count, setCount] = useState(2);
  const shown = events.slice(0, count).reverse();
  return (
    <div className="flex w-full flex-col items-center gap-4">
      <Button variant="secondary" isDisabled={count >= events.length} onPress={() => setCount((n) => n + 1)}>
        Add an event
      </Button>
      <AnimatedList announce className="w-full max-w-sm" aria-label="Recent events">
        {shown.map((text) => (
          <div
            key={text}
            className="rounded-[var(--rd-radius-control)] border border-[var(--rd-color-border-default)] bg-[var(--rd-color-surface-raised)] px-4 py-3 text-sm text-[var(--rd-color-text-default)]"
          >
            {text}
          </div>
        ))}
      </AnimatedList>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
childrenrequired

The items. Each child becomes one list item; give each a stable key so only genuinely new items animate.

nodenone
delay

Seconds between one item appearing and the next, for items that arrive together.

number0.15
duration

Seconds each item takes to slide in.

number0.4
announce

Marks the list as a polite live region so screen readers announce items that are added later. Leave it off for a static list.

booleanfalse
isStill

Shows every item at once with no movement. Moving content should be stoppable.

booleanfalse

Accessibility

Role list, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

Not interactive.

Screen readers announce

  • Read as a list with its number of items
  • With announce on, items added later are read out politely, once, without interrupting

What your code must do

  • A real list: ul with one li per item, so the item count is announced
  • Every item is real DOM in the server HTML and visible without JavaScript or CSS animation
  • Stands still for visitors who ask for less motion (prefers-reduced-motion), and with isStill or inside an rdm-still container
  • Each item moves once and stops, so nothing keeps moving and nothing flashes (WCAG 2.2.2, 2.3.1)
  • Items keep their own focus order and content; the animation never moves focus
  • Optional: it is a separate component, so projects that do not add it carry none of its code

Guidelines

Use it when

  • A feed or activity log where new entries arrive
  • A short list that should feel built up rather than dropped in

Avoid it when

  • Long lists: staggered delays make the last items wait
  • Lists people scan quickly or sort and filter often

Don't

  • A delay so long that content is hidden for several seconds
  • Turning on announce for a list that changes every second

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-text-default