# List animations (/docs/helpers/list-cut)




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 [#install]

```bash
npx shadcn add @keyframery/list-cut
```

If your project doesn't know `@keyframery` yet, do the [Quick start](/docs/installation) first.

## Use [#use]

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

```tsx
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 [#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 [#options]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `as` | `React.ElementType` | `"div"` | The list element. |
| `pace` | `number` |  | Duration multiplier for this list. |
| `cut` | `"none"` |  | none renders changes without motion. |

`ListCut.Item`:

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string \| number` | required | The item's stable id. |
| `as` | `React.ElementType` | `"div"` | The item element. |

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

## Good to know [#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**.


## Questions

### How do I animate adding and removing items in a React list?

Wrap the list in `ListCut` and each item in `ListCut.Item` with the same `id` you use as its `key`. Added items fly in from the button that added them, removed ones fold away, and moved ones glide.

### Does ListCut work with tables?

Yes. Use `ListCut as="tbody"` with `ListCut.Item as="tr"`. A removed row folds inside a one-row table that keeps your column widths.

### Does it work with data from the server?

Yes. ListCut only compares ids between renders, so any data source works. Items that arrive while nothing was pressed rise in.
