Listy is installed into your project as a file you own, and takes the same props as Ant Design's Listy. It replaces Ant Design's older List, which is deprecated and takes different props.
List has item slots, actions, a header, a footer and pagination, and renders every row. Listy handles the scrolling and the grouping, and leaves the content of every row to you. It can virtualize because it never needs to know what a row looks like.
rowKey and itemRender are required. Everything else is optional.
Install
npx antuikit@latest init
npx antuikit@latest add listy
The first command installs AntuikitProvider and the theme tokens, and the second adds Listy.
Import
Import from @/components/ui/listy, not from antd. The file is yours once installed, so the path points into your project. Import the ListyRef type from antd, since the kit does not re-export it.
import { Listy } from "@/components/ui/listy"
The axes
Listy has no size, variant or shape. Its only set of named values is align, on the scroll handle.
align, inside a scrollTo config, takes top, bottom or auto. The config itself has four shapes: a bare index, { key, align, offset }, { groupKey, align, offset } or { top, left }.
There is no direction prop. The reading direction comes from AntuikitProvider, so the list flips with the page.
There is no itemHeight prop. Rows measure their own height, with or without virtual.
virtual and sticky need a height. The height gives the list its own scroll area. Without it, virtual renders every row and sticky has no list scroll to pin headers in.
virtual renders only the rows on screen, and is off by default. Off-screen rows are not in the page, so :last-child matches the last rendered row and a row outside the window cannot take a drop.
sticky pins each group header until the next one pushes it out, so it needs group as well as height. The default header is opaque. Keep a custom classNames.groupHeader background opaque too, or rows show through a pinned header.
Slots
Listy has no named slots. Two render functions return whole nodes instead.
itemRender(item, index) returns the whole row. A rich row is kit components you compose, like Avatar, Flex, Typography and Tag.
group.title(groupKey, items) returns the group header. It receives the group's rows as well as its key, so a count or a subtotal costs nothing extra.
TypeScript infers the item type from items, so itemRender gets a typed item and rowKey only accepts that item's fields. The second type parameter is the group key's type, inferred from group.key, and it types title's first argument. Writing either one out is optional.
Events
onScroll
The parts
Listy has no subcomponents such as Listy.Item. Its parts are three elements it builds itself, named by the keys classNames and styles share.
root is the scroll container. height puts max-height and overflow-y: auto on it, and styles.root merges over both.
item is the element Listy puts around whatever itemRender returns. It already has padding, a bottom separator and a hover color, so use this key to change them.
groupHeader is the header group.title renders into, and the element sticky pins.
Rows return bare content, so padding on your row doubles it. To drop the last separator, set classNames={{ item: "last:border-0" }}. The same class on your row never matches, and with virtual on, leave it out.
Styling
classNames and styles take an object keyed root, item and groupHeader, or a function that returns one. className, rootClassName and style land on the root.
classNames and styles functions see the whole list. Their one argument is { props } for the list, not for a row, so use them for list-wide state like “anything selected”. Style a single row from its own state inside itemRender.
API Reference
Listy
Listy's props. Rows marked antuikit in the Version column are added by the kit.