ComponentsPage layouts

List layout

The standard index page — Page header, a sticky toolbar (search, filters, saved views, view switch), a bulk-action bar that takes the toolbar's place, the content (table, grid or board) and footer pagination — with one consistent set of loading, refreshing, empty, no-results and error states. It arranges and announces; filtering, selection and data fetching stay with your Data Table and data layer.

Radix Toggle Group (view switch) · Sheet (mobile filters) components/list-layout.tsx, styles/navigation.csstheme · material · accent · density · font

Contacts

1,284
NameStageOwnerValue
Laura CallahanNorthwind TradersCustomerMeltem Aydın$48,500
Jonas WeberKessler LogistikProposalCan Demir$22,000
Priya NairLotus HealthQualifiedMeltem Aydın$15,750
Kenji SatoHikari StudioLeadEce Yılmaz$6,200
Amara OkaforBaobab EnergyCustomerCan Demir$91,300

1–20 of 1,284

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { ListLayout, ListToolbar, ListViewSwitch, ListBulkBar, ListContent, ListFooter, PageHeader, PaginationBar } from "@vamos-ui/react";

<ListLayout>
  <PageHeader title="Contacts" meta={<Badge>1,284</Badge>} actions={<Button variant="primary">New contact</Button>} />
  <ListToolbar
    search={<Input leading={<Search />} placeholder="Search contacts" aria-label="Search contacts" />}
    filters={<FilterBar … />}
    filterCount={2}
    viewSwitch={<ListViewSwitch value={view} onValueChange={setView} options={[{ value: "table", label: "Table" }, { value: "board", label: "Board" }]} />}
  />
  <ListBulkBar count={selected.length} total={1284} onSelectAll={selectAll} onClear={clear}>
    <Button size="sm">Assign owner</Button>
  </ListBulkBar>
  <ListContent state={state} resultCount={1284} onRetry={refetch} onClearFilters={clearFilters}>
    <DataTable … />
  </ListContent>
  <ListFooter><PaginationBar total={1284} page={page} pageSize={20} onPageChange={setPage} /></ListFooter>
</ListLayout>

Selection lives in your Data Table; ListBulkBar is presentational. The same goes for filters and saved views.

Built-in strings use the list namespace: selected, selectAllCount, allSelected, clear, filters, filtersCount, results, refreshing, noResults, clearFilters, error, retry.

Examples

States

Every ListContentState. refreshing keeps the rows and shows a thin bar; the change is announced in a polite live region ("3 results"). Override any placeholder with empty / noResults / error / skeleton.

Selection and bulk bar

The bar replaces the toolbar while count > 0 and announces "2 selected". "Select all 312" widens the selection across pages (allSelected). Esc clears and focus returns to the toolbar.

2 selected
2 selected
  • Selin ÖztürkProduct designerDesign
  • Marcus LindqvistBackend engineerPlatform
  • Aylin ÇelikPeople partnerPeople
  • Tomás RibeiroAccount executiveSales

Grid view

ListViewSwitch is a radio group with one tab stop; arrow keys move, Space selects. A CMS media library with a "Load more" footer.

Media

6 of 418 files · 1.9 GB used

  • dining-room-dusk.jpg

    2.4 MB

  • menu-spring-2027.pdf

    860 KB

  • team-offsite.jpg

    3.1 MB

  • logo-mark.svg

    12 KB

  • grilled-sea-bass.webp

    1.2 MB

  • studio-window.jpg

    2.9 MB

Showing 6 of 418

Phone width

The layout responds to its own width: below ~640px filters fold into a "Filters (3)" sheet and the view switch shows icons only.

Partners

  • Atlas Distribution

    Gold · EMEA

  • Brightline Resellers

    Silver · North America

  • Kumo Partners

    Gold · APAC

API

ListLayout

PropTypeDefault
width
Max width: none · 90rem · 76rem.
"full" | "wide" | "content""full"
stickyToolbar
Toolbar and bulk bar stick under the top bar on the bar surface.
booleantrue
stickyTop
Sticky offset. Use 0 inside your own scroll container.
number | string"var(--topbar-h)"

ListToolbar

PropTypeDefault
search
Search field, wrapped in a role="search" landmark.
ReactNode—
searchLabel
Name of the search landmark.
string"Search"
filters
Filter chips / Filter bar. Fold into a sheet below ~640px.
ReactNode—
filterCount
Active filters, shown on the folded "Filters (3)" button.
number—
views
Saved views — a row above the search.
ReactNode—
viewSwitch
Usually a ListViewSwitch.
ReactNode—
actions
Trailing actions: export, columns, ⋯.
ReactNode—
labels
Overrides for the list namespace.
ListLayoutLabels—

ListViewSwitch

PropTypeDefault
options
Views. With an icon the label hides on narrow lists (still the accessible name).
{ value: string; label: string; icon?: ReactNode }[]—
value · defaultValue · onValueChange
Controlled or uncontrolled. A view is always selected.
string—
label
Accessible name of the radio group.
string"View"

ListBulkBar

PropTypeDefault
count
Selected rows. The bar shows while count > 0.
number—
total
All matching rows; enables "Select all {total}".
number—
onSelectAll
Select every matching row across pages.
() => void—
allSelected
Shows "All {total} selected".
boolean—
onClear
Clear button and Esc.
() => void—
children
Bulk actions.
ReactNode—

ListContent

PropTypeDefault
state
What the content area shows; loading and refreshing set aria-busy.
"ready" | "loading" | "refreshing" | "empty" | "no-results" | "error""ready"
empty · noResults · error · skeleton
Replace the default placeholders.
ReactNode—
onRetry
Adds "Try again" to the default error state.
() => void—
onClearFilters
Adds "Clear filters" to the default no-results state.
() => void—
resultCount
Announced politely when the list becomes ready.
number—
skeletonRows
Rows in the default skeleton.
number6

ListFooter

PropTypeDefault
className
Row under the content: PaginationBar, Load more, a summary.
string—