Skip to content

ComponentsCollections

Grid List

An interactive list whose rows can hold buttons, menus and links. Supports single and multiple selection with checkboxes, row actions, links, drag-to-reorder and drag between lists, card grid layouts, infinite loading and empty states.

React AriaSource
Q3 report.pdf2.4 MB · Edited 2h ago
Budget 2027.xlsx860 KB · Edited yesterday
Hero banner.png1.1 MB · Edited Sep 12

Installation

pnpm dlx shadcn@latest add @desyne/grid-list

The CLI installs dependencies and any other components this one uses.

Usage

tsx
import {
  GridList,
  GridListItem,
  GridListItemDescription,
  GridListItemLabel,
} from "@/components/ui/grid-list";
tsx
<GridList aria-label="Files" selectionMode="multiple">
  <GridListItem id="report">Q3 report.pdf</GridListItem>
  <GridListItem id="budget">Budget 2027.xlsx</GridListItem>
</GridList>

Rows can contain controls

Unlike List Box, grid list rows are role="row" and may contain buttons, menus, switches and links. Arrow keys move between rows, and ← / → move into a row's controls. Selection checkboxes and drag handles are added for you.

When to use

  • Grid List — a list of objects users act on: files, members, notifications, tasks, cards.
  • List Box — choosing values from plain options, with no controls inside rows.
  • Table — multiple columns of comparable data, sorting and column headers.
  • Menu — a transient list of commands.

Anatomy

tsx
<GridList>                         {/* role="grid" */}
  <GridListItem>                   {/* role="row" */}
    {/* drag handle, when dragAndDropHooks is set */}
    {/* selection checkbox, when selectionMode is set */}
    <Icon />
    <GridListItemLabel />
    <GridListItemDescription />
    <Button />                     {/* any interactive content */}
  </GridListItem>
</GridList>
PartRendersNotes
GridList<div role="grid">Scrollable list. variant sets the frame. Shows renderEmptyState when empty.
GridListItem<div role="row"> › <div role="gridcell">A row. Plain-string children become its textValue.
Drag handle<button slot="drag">Grip icon, rendered automatically when the list has dragAndDropHooks.
Selection checkboxCheckboxRendered automatically when selectionMode is set and selectionBehavior is toggle.
GridListItemLabel<span>Bold, truncated primary text.
GridListItemDescription<span slot="description">Muted secondary text, linked as the row's description.

Examples

Variants

bordered (default) frames the list as a card, separated draws a divider between rows, and plain has no frame for embedding in your own container.

bordered
Inbox
Drafts
Sent
separated
Inbox
Drafts
Sent
plain
Inbox
Drafts
Sent

Single selection

selectionMode="single" adds a checkbox that behaves like a radio. disallowEmptySelection keeps one row selected.

Visa ending 4242Expires 08/28
Amex ending 1005Expires 02/27
Business checkingACH · ending 6789
Account credit$120.00 available

Controlled selection

selectedKeys and onSelectionChange own the selection. Handle both a Set of keys and "all" (after ⌘/Ctrl+A or the select-all shortcut).

SlackPost alerts to #incidents
GitHubLink commits and pull requests
LinearCreate issues from alerts
PagerDutyPage the on-call engineer
DatadogImport metrics and monitors
2 of 5 enabled

Selection behavior

selectionBehavior="replace" hides the checkboxes and selects like a file browser: a click selects one row, ⌘/Ctrl or Shift add more.

Contracts
Invoices
Receipts
Tax returns
Payroll
Archive

No checkboxes: click to select, ⌘/Ctrl or Shift to select more.

Disabled rows

disabledKeys disables rows. With disabledBehavior="selection" they can't be selected but stay focusable, so keyboard and screen reader users can still reach them and their contents. The default all disables every interaction.

Ana Souzaana@northwind.ioOwner
Kenji Watanabekenji@northwind.io
Sam Okaforsam@northwind.io
Inès Laurentines@northwind.io

Row actions and controls

onAction fires when a row is clicked or Enter is pressed. Buttons and menus inside the row handle their own presses without triggering the row action.

Onboarding flowEdited 2h ago
Pricing page redesignEdited yesterday
Mobile navEdited Sep 20

Give rows an href to make the whole row a link. They work with client-side routers through React Aria's RouterProvider.

InstallationAdd the library to a Next.js app
ThemingCustomize tokens, colors and radius
ComponentsBrowse every component

Empty state

renderEmptyState renders when there are no items. The list gets data-empty and centered, muted text.

No saved searches

Save a search from the results page to get back to it quickly.

Drag to reorder

Pass dragAndDropHooks from useDragAndDrop and each row gets a drag handle. Update your data in onReorder, and use renderDropIndicator with React Aria's DropIndicator to show where items will land. Keyboard users focus the handle and press Enter to start dragging.

Triage new alerts
Review access requests
Rotate API keys
Update runbooks
Plan the next game day

Grid layout

layout="grid" enables two-dimensional arrow key navigation. Combine it with variant="plain" and CSS grid classes to build a card picker; restyle rows with className.

BlankStart from scratch
Landing pageHero, features, pricing
BlogPosts and categories
DocsSidebar and search
StoreProducts and cart
PortfolioCase studies

Infinite loading

Render the items in a Collection followed by GridListLoadMoreItem, which calls onLoadMore as it scrolls into view. useAsyncList handles cursors and loading state. Give the list a fixed height.

No members.

Recipes

File manager with bulk actions

A plain grid list inside a card, with a toolbar that switches to bulk actions while rows are selected.

5 files
Q3 board deck.pdf4.2 MB
Revenue forecast.xlsx860 KB
Team offsite.jpg2.8 MB
Hiring plan.pdf310 KB
Churn analysis.xlsx1.3 MB

Kanban board

Three lists that share a custom drag type. onInsert and onRootDrop accept items from other columns, onReorder moves within a column, and onDragEnd removes moved items from the source.

To do3
Audit onboarding emailsMedium
Fix CSV export timeoutHigh
Refresh pricing screenshotsLow
In progress1
SSO for enterprise workspacesHigh
Done1
Migrate to the new billing APIMedium

Notification inbox

A separated list where onAction marks a notification as read. The unread dot has visually hidden text so it's announced.

Notifications (2)
Maya requested your reviewfeat: usage-based billing · 5mUnread
Leo mentioned you in #design“Can you check the empty states?” · 1hUnread
New comment on Q3 roadmapSam: Moved SSO to October · 3h
Your pull request was approvedfix: date picker focus · yesterday

Accessibility

  • The list is role="grid" and each item is a role="row" with a single gridcell, so screen readers announce row position and selection.
  • Always provide aria-label or aria-labelledby.
  • Focus is a single tab stop with arrow key navigation between rows. Interactive children are reached with ← / → (or Tab with keyboardNavigationBehavior="tab").
  • Give icon-only buttons inside rows an aria-label, and make sure row text is available as textValue for typeahead and announcements.
  • Selection checkboxes are labelled from the row automatically.
  • Drag and drop has built-in keyboard and screen reader support, with live announcements for each step.

Keyboard

KeyAction
TabMoves focus into and out of the list
↑ / ↓Moves focus to the previous / next row (all four arrows in grid layout)
← / →Moves focus between the row and its interactive children
Home / EndMoves focus to the first / last row
Page Up / Page DownMoves focus by a page
SpaceToggles selection of the focused row
EnterTriggers onAction, follows a link, or starts a drag from the handle
Shift+↑ / Shift+↓Extends the selection (multiple)
⌘/Ctrl+ASelects all (multiple)
EscClears the selection, or cancels a drag
Any characterTypeahead (unless disallowTypeAhead)

Styling

Data attributes

On the GridList (use group-data-*/list: inside it):

AttributePresent when
data-variantAlways: bordered, separated or plain
data-emptyThere are no items
data-focused / data-focus-visibleThe list itself has focus / keyboard focus
data-drop-targetSomething is being dragged over the list
data-layoutAlways: stack or grid
data-orientationAlways: vertical or horizontal

On each GridListItem (use group-data-*/item: inside it):

AttributePresent when
data-selectedThe row is selected
data-focused / data-focus-visibleThe row has focus / keyboard focus
data-focus-visible-withinA control inside the row has keyboard focus
data-hovered / data-pressedHovered with a mouse / being pressed
data-disabledThe row is disabled
data-selection-modesingle or multiple
data-allows-dragging / data-draggingDrag and drop is enabled / the row is being dragged
data-drop-targetThe row is the current drop target

Slots

data-slotElement
grid-listRoot
grid-list-itemEach row
grid-list-item-labelGridListItemLabel
  • The list has max-h-[inherit] and overflow-auto; give it a max-h-* or h-* to scroll.
  • Rows are flex items-center gap-3, so icons, text and trailing controls line up without extra wrappers. Give the text block flex-1 to push controls to the end.
  • className on GridList and GridListItem also accepts a function of the render props, e.g. ({ isSelected }) => ….

API Reference

GridList

Prop

Type

Also accepts every prop of React Aria's GridList.

GridListItem

Prop

Type

GridListItemLabel

Accepts every prop of <span>.

GridListItemDescription

Accepts every prop of React Aria's Text. It sets slot="description".

  • List Box — selectable options without interactive content.
  • Table — rows and columns with sorting.
  • Menu — for per-row action menus.
  • Checkbox — the selection control used in rows.