Skip to content
Redpanda UI
Esc
↑↓navigate↵open⌘Jpreview
Made by eblairmckee
Edit on GitHub

ListView

A CSS Grid-based horizontal layout for displaying items with start, intermediary, and end slots.

Installation

bash
npx shadcn@latest add @redpanda/list-view
bash
pnpm dlx shadcn@latest add @redpanda/list-view
bash
yarn dlx shadcn@latest add @redpanda/list-view
bash
bunx shadcn@latest add @redpanda/list-view
bash
nubx shadcn@latest add @redpanda/list-view
bash
aube dlx shadcn@latest add @redpanda/list-view

Usage

ListView is a presentational three-column grid. Put a native action, such as a Button, Link, Switch, or menu trigger, in ListViewEnd. The row itself is not clickable or focusable.

tsx
import {  ListView,  ListViewGroup,  ListViewStart,  ListViewIntermediary,  ListViewEnd,} from '@/components/redpanda-ui/list-view';

Anatomy

The ListView component is built with a three-column grid system:

plaintext
ListView Structure:┌─────────────────────────────────────────────────────────────────────────┐│ <ListViewGroup> (Container for multiple ListView items)                 ││ ┌─────────────────────────────────────────────────────────────────────┐ ││ │ <ListView> (Single row with 3-column grid)                          │ ││ │ ┌──────────────────┬─────────────────────┬────────────────────────┐ │ ││ │ │ <ListViewStart>  │ <ListViewIntermed>  │ <ListViewEnd>          │ │ ││ │ │ • Title          │ • Badges            │ • Single action        │ │ ││ │ │ • Description    │ • Tags              │ • Switch/Button        │ │ ││ │ │ • Metadata       │ • Status indicators │ • Right-aligned        │ │ ││ │ │                  │ • Hidden on mobile  │                        │ │ ││ │ └──────────────────┴─────────────────────┴────────────────────────┘ │ ││ └─────────────────────────────────────────────────────────────────────┘ ││ ┌─────────────────────────────────────────────────────────────────────┐ ││ │ <ListView> (Next row - border-top auto-applied in grouped)          │ ││ └─────────────────────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────────────────┘
Grid Column Sizes:• Col 1 (Start): minmax(40%, 2fr) - Primary content, shrinks gracefully• Col 2 (Intermediary): 2fr - Secondary content, hidden below lg breakpoint• Col 3 (End): 1fr - Actions, right-aligned, always visible

Slot Guidelines

ListViewStart (Column 1)

Purpose: Primary content - the main information users need to identify the item.

Best practices:

  • Use title prop for the item name (auto-wrapped in a semantic h3)
  • Use description prop for secondary info (styled with text-body text-subtle)
  • Keep titles concise - they truncate on overflow
  • Use custom children for complex layouts (icons + text)
tsx
// Simple: title and description props<ListViewStart  title="Claude 3.5 Sonnet"  description="Anthropic's most intelligent model"/>
// Complex: custom children<ListViewStart>  <div className="flex items-center gap-2">    <AnthropicIcon className="size-4" />    <h4 className="text-heading-sm">Claude 3.5 Sonnet</h4>  </div>  <p className="text-body text-subtle">claude-3-5-sonnet-20241022</p></ListViewStart>

ListViewIntermediary (Column 2)

Purpose: Secondary metadata - badges, tags, status indicators that provide additional context.

Key behavior:

  • Hidden below the lg breakpoint (1024px)
  • Left-aligned within its column
  • Use for non-critical information that enhances the view

Best practices:

  • Use BadgeGroup for multiple badges with overflow handling
  • Keep content minimal - this column disappears on smaller screens
  • Don’t put critical actions here
tsx
<ListViewIntermediary gap="md">  <Badge tone="success" variant="subtle">    Active  </Badge>  <BadgeGroup    maxVisible={2}    items={[      {        id: 'React',        textValue: 'React',        content: <Badge size="sm">React</Badge>,      },      {        id: 'TypeScript',        textValue: 'TypeScript',        content: <Badge size="sm">TypeScript</Badge>,      },      {        id: 'Tailwind',        textValue: 'Tailwind',        content: <Badge size="sm">Tailwind</Badge>,      },    ]}  /></ListViewIntermediary>

ListViewEnd (Column 3)

Purpose: Single action - the primary interactive element for the item.

Common action patterns:

  • Switch - Toggle enable/disable state
  • Link - Navigate to a detail view
  • Button - Select an item or open a panel
  • Icon button (menu) - Open dropdown with multiple actions
tsx
// Toggle action<ListViewEnd>  <Switch aria-label="Enable model" checked={enabled} onCheckedChange={setEnabled} /></ListViewEnd>
// Selection action: the pressed state belongs to the native button<ListViewEnd>  <Button    aria-pressed={selected}    onClick={() => setSelected((current) => !current)}    type="button"    variant="ghost"  >    Select model  </Button></ListViewEnd>
// Menu action<ListViewEnd>  <DropdownMenu>    <DropdownMenuTrigger      render={        <Button aria-label="Model actions" size="icon-sm" type="button" variant="ghost">          <MoreHorizontalIcon className="size-4" />        </Button>      }    />    <DropdownMenuContent align="end">      <DropdownMenuItem>Edit</DropdownMenuItem>      <DropdownMenuItem>Duplicate</DropdownMenuItem>      <DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>    </DropdownMenuContent>  </DropdownMenu></ListViewEnd>

Interaction

ListView displays information; its children own interaction. Clicking a title or description does not activate the End action. Native controls provide their own focus, keyboard, disabled, and navigation behavior without nesting pressable containers.

tsx
<ListView>  <ListViewStart title="Model name" description="Provider" />  <ListViewEnd>    <Switch aria-label="Enable model" checked={enabled} onCheckedChange={setEnabled} />  </ListViewEnd></ListView>

Use an application Link in End for navigation, a Button for an action, or a DropdownMenu for multiple actions. Keep essential actions out of Intermediary because that slot is hidden on smaller screens.

Breaking change: presentational root

Remove interactive, all root event handlers, role, tabIndex, and contentEditable. Move behavior to an explicit native control in End rather than attaching it to the row. Whole-row hover and pointer styles have been removed.

The root accepts children, className, id, ref, style, title, hidden, inert, dir, lang, size, variant, and testId. Other DOM props are not forwarded, including legacy props received through a spread. The ref still targets the layout’s HTMLDivElement; slot and group APIs are unchanged.

Variants

Grouped (Default)

Use ListViewGroup to group multiple items with shared borders and automatic dividers:

tsx
<ListViewGroup variant="outlined">  <ListView>...</ListView>  <ListView>...</ListView>  <ListView>...</ListView></ListViewGroup>

Standalone

For single items, use the outlined or elevated variant directly:

tsx
<ListView variant="outlined">  <ListViewStart title="Single Item" />  <ListViewEnd>    <Button onClick={openDetails} type="button">      View details    </Button>  </ListViewEnd></ListView>

Sizes

Control padding and gap with the size prop:

tsx
<ListView size="sm">Compact padding (px-4 py-3)</ListView><ListView size="md">Standard padding (px-5 py-4)</ListView><ListView size="lg">Generous padding (px-6 py-5)</ListView>

Combining with ListCard

ListView and ListCard share the same data model, making it easy to offer users a choice between list and grid views.

Examples

Basic List

List/Card Toggle

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
  • minorv0.3.0Add theme-provider component to the registry with documentation and tests. Includes playground type improvements (export RegistryItem, remove as-const boilerplate) and docs site dark mode border color fix.#109
See full history →

Last updated on

Was this page helpful?