Search the docs

Search Keyframery's docs

Keyframery

Lists

ListCut: new items fly in from the button that added them, removed ones fold away, and moved ones glide.

with <Cuts />
  • Write the release notes
  • Review the onboarding flow
  • Reply to Priya

In a stock list, a new item appears from nowhere, a deleted one vanishes and everything below jumps up, and a reorder teleports every row. ListCut gives each of those changes a motion you can follow.

Install

npx shadcn add @keyframery/list-cut

If your project doesn't know @keyframery yet, do the Quick start first.

Use

Replace your list and its items with ListCut and ListCut.Item, and give each item the same id you use as its key:

import { ListCut } from "@/components/keyframery/list-cut"

<ListCut as="ul" className="space-y-2">
  {messages.map((m) => (
    <ListCut.Item key={m.id} id={m.id} as="li">
      {m.text}
    </ListCut.Item>
  ))}
</ListCut>

It works with any data source, because it only compares ids between renders. For a table, use ListCut as="tbody" with ListCut.Item as="tr".

You should see

  • Add an item with a button: it flies out of that button into its place.
  • Add an item with no button involved, for example from the server: it rises in.
  • Remove an item: it folds away, and the items below slide up a moment later.
  • Reorder: every item glides to its new place.

Options

Prop

Type

ListCut.Item:

Prop

Type

Both pass any other props, such as className or aria-label, to their element.

Good to know

  • The first render never animates.
  • Only items in or near the viewport animate, so long lists stay fast.
  • Timing: an added item flies in over 480 ms; a removed one folds away and the items below follow 60 ms later; a moved one glides over 320 ms.
  • A removed table row folds inside a one-row table that keeps your column widths.

In film, a new item flying from the button is a cut on action, and a removed item leaving before the rest move up is an L-cut.

On this page