Skip to content

List view

A list of records: list views, search and a table to sort and select. Lightning's object home, for a standard or custom object.

Accounts

Select a list view

10+ items • Sorted by Account Name • Filtered by All accounts • Updated a few seconds ago

Sorted by Account Name, ascending
Row NumberActions
1
Customer - Direct
Ellen Hart
Jun 12, 2026, 10:42 AM
2
Customer - Channel
Ellen Hart
Jun 3, 2026, 1:10 PM
3
Customer - Direct
Ellen Hart
Jun 12, 2026, 9:05 AM
4
Customer - Channel
Priya Raman
May 29, 2026, 3:31 PM
5
Customer - Channel
Priya Raman
Jun 10, 2026, 11:48 AM
6
Customer - Direct
Ellen Hart
Jun 8, 2026, 8:22 AM
7
Customer - Channel
Priya Raman
May 21, 2026, 4:15 AM
8
Ellen Hart
Apr 30, 2026, 12:02 PM
9
Customer - Direct
Ellen Hart
Jun 11, 2026, 2:40 PM
10
Customer - Direct
Priya Raman
Jun 1, 2026, 10:55 PM

Installation

pnpm dlx shadcn@latest add @wisty/list-view

The CLI installs the Radix or Base UI file to match your project, with what it builds on: data-table, list-view-header.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • For an object's tab in an app, standard or custom: its records with Lightning's list view switcher, search, row numbers, sortable columns, selection and a menu on each row.
  • When you want Lightning's object home as a page you own: the Accounts page installs as a file, and another object's page is the same lines with its own columns, icon and label.
  • When the app reads the records, a page at a time or all at once, and passes them in as plain rows.

Don't use it

Usage

import { DataTable } from "@/components/wisty/data-table"
import { ListView, useListView } from "@/components/wisty/list-view"
import { ListViewHeader } from "@/components/wisty/list-view-header"
const list = useListView({
  records: cases,
  searchBy: ["caseNumber", "subject", "status"],
  columns,
  getRowId: (record) => record.id,
  loading,
})
<ListView>
  <ListViewHeader
    icon="standard:case"
    objectLabel="Cases"
    views={views}
    itemCount={list.itemCount}
    sortedBy={list.sortedBy}
    search={list.search}
    onSearchChange={list.setSearch}
    loading={loading}
    joined
  />
  <DataTable
    table={list.table}
    aria-label="All Open Cases"
    isLoading={loading}
    empty={list.unavailable ? "Cases aren't available." : undefined}
  />
</ListView>

ListView is the frame: it joins a List view header to the Data table under it. useListView takes Data table's options with records in place of data, filters the records by the search over the searchBy fields, counts what's left, names the column the table sorts by, and tells rows that couldn't load from none. The Accounts page, ListViewPage, installs as list-view-page.tsx, the file you edit: swap its fixture import for the app's reads, and pass onAction, onRowAction and the handler of each list control the app supports. To search on the server, leave searchBy out, read list.search and pass the results in as records. To act on the selected rows, read list.table.getSelectedRowIds() beside the header's actions.

Examples

Custom object

Projects, a custom object, written out in full: its own icon, list views and columns, with a currency, a percent and empty values.

Projects

Select a list view

9 items • Sorted by Project Name • Filtered by All projects • Updated a few seconds ago

Sorted by Project Name, ascending
Row NumberActions
1
Burlington Textiles Corp of America
Completed
$235,000.00
100%
Jul 6, 2026
Ellen Hart
2
Dickenson plc
Planned
$15,000.00
0%
Jan 12, 2027
Ellen Hart
3
Edge Communications
In Progress
$75,000.00
35%
Oct 5, 2026
Ellen Hart
4
Edge Communications
Completed
$50,000.00
100%
Aug 17, 2026
Priya Raman
5
Express Logistics and Transport
Completed
$220,000.00
100%
Jun 1, 2026
Priya Raman
6
GenePoint
Planned
$60,000.00
0%
Ellen Hart
7
In Progress
$12,000.00
50%
Oct 1, 2026
Priya Raman
8
Grand Hotels & Resorts Ltd
In Progress
$210,000.00
60%
Sep 14, 2026
Marcus Lee
9
University of Arizona
On Hold
20%
Aug 24, 2026
Marcus Lee

Unavailable

With `records` null, the count and the Updated time drop out and the table says so, where an empty array reads 0 items.

Accounts

Select a list view

Sorted by Account Name • Filtered by All accounts

Sorted by Account Name, ascending
Row Number
Accounts aren't available.

API reference

ListView

The frame that joins a list view header to the table under it.

Also takes every <div> (opens in a new tab) prop.

useListViewhook

Holds the list's search and derives its count, sort label and unavailable state from the app's rows.

ParameterTypeDefault
options*UseListViewOptions<TRecord>

Returns UseListViewResult<TRecord>.

UseListViewOptionstype

MemberTypeDefaultDescription
records*TRecord[] | nullThe rows the app has read, or null when they couldn't load.
searchByreadonly (keyof TRecord)[]The fields the search matches; leave it out to search on the server.
loadingbooleanThe first read: no count while it lasts, and never unavailable.
getRowId*( originalRow: TData, index: number, parent?: DataTableRow<TData> ) => stringA stable id per row, like a record id. Selection is kept by it.
showRowNumberColumnbooleanAdds a row number column first; the numbers follow the rows shown, so sorting renumbers them.
rowNumberOffsetnumberThe rows before the first: with 10, the first row reads 11. Defaults to 0.
maxRowSelectionnumberCaps the selection in every kind of change; 1 renders radios. Implies enableRowSelection.
getRowLabel(row: Row<TFeatures, TData>) => stringNames a row in its labels, like "Select Acme". Defaults to the row header's value.
rowActions| readonly DataTableRowAction[] | ((row: Row<TFeatures, TData>) => readonly DataTableRowAction[])The actions menu of each row, or a function that returns a row's actions when its menu opens.
onRowAction(action: DataTableRowAction, row: Row<TFeatures, TData>) => voidCalled with the action picked from a row's menu.
onHeaderAction( action: DataTableHeaderAction, column: Column<TFeatures, TData, unknown> ) => voidCalled with the action picked from a column header's menu.
localestringA BCP 47 locale for numbers, dates and sorting text. Defaults to "en-US".
timeZonestringAn IANA time zone for "datetime" values. Defaults to "UTC".
nowstring | number | DateThe instant "relative" values count from. Defaults to render time; pass one for a stable render.

Also has every tanstack/table-core member.

UseListViewResulttype

MemberTypeDefaultDescription
table*DataTableInstance<TRecord>The table for DataTable, over the rows the search leaves.
search*stringThe search text, for ListViewHeader's search.
setSearch*(search: string) => voidSets the search text, for ListViewHeader's onSearchChange.
itemCount*number | nullHow many rows the search leaves; null while loading or unavailable.
sortedBy*stringThe label of the column sorted first, if any, for ListViewHeader's sortedBy.
unavailable*booleanThe rows couldn't load: records is null and nothing is loading.

SLDS mapping

The page is Lightning's object home: List view header (the page header's object home variant) joined to Data table in one frame, the way a list view draws its table under the header.

Known gaps:

  • No kanban or split view: the page draws a table, so it passes no onDisplayChange, and Display as doesn't show.
  • No inline edit in the cells, no list view charts and no filter panel.

Responsive behavior

  • The frame takes its container's width. The header's controls move under the meta line when they don't fit, and the table scrolls sideways in a narrow container.

Rules

  • Pass null to records when the list couldn't load, never []: an empty array says the list view has no records.
  • Pass loading to useListView and ListViewHeader, and as isLoading to DataTable, while the first rows are read.
  • While the app has read only some rows, pass hasMoreItems to the header and hasNextPage to the table. The count then reads "10+ items", and Load more asks for the rest.
  • Selection outlives a search that hides selected rows. Act only on the selected rows the table shows, or call list.table.resetRowSelection() when the search changes.
  • Pass updated as the page writes it, from the time the rows were read, and leave it out while list.unavailable. The page never reads the clock.
  • Render one per page: the list view's name is the page's h1. Never pass asChild or render.