List
The page that fetches and displays a page of records for a resource.
The <List> view is where users browse a resource. It runs the list controller
for the ambient <Resource>, fetches the current page of records, and publishes
them through a list context that every descendant reads: the <DataTable>,
the pagination, the filter bar. You compose what to show; <List> owns the
fetch and the URL-synced query state (page, sort, filters).
A live list
Here is the posts list, running against the in-memory provider: a real
<DataTable> over 30 seeded records, paged ten at a time by <ListPagination>.
import {
BooleanField,
DataTable,
EditButton,
List,
ListPagination,
ReferenceField,
TextField,
} from "@chelone/react";
export function PostList() {
return (
<List>
<DataTable>
<DataTable.Col source="title" />
<DataTable.Col label="Author">
<ReferenceField source="author_id" reference="authors">
<TextField source="name" />
</ReferenceField>
</DataTable.Col>
<DataTable.NumberCol source="views" />
<DataTable.Col source="published">
<BooleanField source="published" />
</DataTable.Col>
<DataTable.Col>
<EditButton />
</DataTable.Col>
</DataTable>
<ListPagination />
</List>
);
}<List> renders only what you give it. Without
<ListPagination> the list stays on page 1 and
the user cannot reach the other pages or change the page size.
Wire it onto the resource so /posts renders it:
import { Resource } from "@chelone/react";
import { PostList } from "./posts";
<Resource name="posts" list={PostList} />;Clicking a row opens its edit page: <DataTable> defaults rowClick to
"edit". Pass rowClick="show", a path, a function, or false to change it.
How it works
<List> reads the resource from the surrounding <Resource> context (or from a
resource prop) and calls useListController. The controller keeps the query
(current page, perPage, sort, and the active filters) in sync with the URL, so
a shared link reproduces the exact view. The fetched records, the loading flag,
and the selection are handed to the descendants through the list context; the
<DataTable> renders the rows, and its column headers drive sorting.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| actions | false | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Header actions (create/export/filter buttons); nothing by default. |
| children | ReactNode | — | |
| className | string | — | |
| component | ElementType<any, keyof IntrinsicElements> | — | Element/component wrapping the children (defaults to `"div"`). |
| perPage | number | — | Rows-per-page for the initial query. |
| resource | string | — | Override the ambient resource (defaults to the `<Resource>` context). |
| title | ReactNode | — | Header title. A string/element renders as an `<h2>`; `false` omits it. |
<List> takes resource, perPage, title, actions, component,
className and children. Nothing else is forwarded to the controller. For
anything more (default filters, a permanent filter, a default sort, a
different store key) call useListController yourself and provide the result
with ListContextProvider. ListView is the same title, actions and body
layout <List> renders. It reads the resource from context, not from the
controller, so render the component inside a <Resource> or a
ResourceContextProvider, or ListView throws:
import {
DataTable,
ListContextProvider,
ListPagination,
ListView,
useListController,
} from "@chelone/react";
export function PublishedPostList() {
const controller = useListController("posts", {
perPage: 25,
sort: { field: "views", order: "DESC" },
filterDefaultValues: { published: true },
});
return (
<ListContextProvider value={controller}>
<ListView title="Published posts">
<DataTable>
<DataTable.Col source="title" />
<DataTable.NumberCol source="views" />
</DataTable>
<ListPagination />
</ListView>
</ListContextProvider>
);
}See Advanced for every controller
option. For a feed that appends pages instead of replacing them, use
<InfiniteList>.
URL sync
The controller writes the list query to the URL and reads it back, so a list is a link you can share.
- On by default for a top-level list, and active whenever the list renders
under a router (inside
<Admin>it does). PassuseListController("posts", { syncWithLocation: false })for an embedded or reference list that should keep its query out of the URL. - The params are
filteranddisplayedFilters(both JSON),sort,order(ASCorDESC),pageandperPage. Malformed values are ignored. The URL carries the primary sort only. - Opening a URL that carries a custom param seeds the first fetch from it. A
param is custom when it sets a filter with at least one key, a
sortororder, aperPage, or apageother than 1. A URL without one, such as/posts,/posts?page=1,/posts?filter={}or one with onlydisplayedFilters, is treated as clean: the saved params, then the defaults, apply, even though the address bar shows the URL. - A change to page, sort, filters or page size replaces the current history entry. It does not push a new one, so the back button leaves the list rather than stepping through each page or keystroke. The mount itself writes nothing: a clean URL stays clean until the user changes something.
- If the location changes after the list has mounted (a followed link, or back
and forward between two URLs of the same list) a URL with custom params
re-seeds the list from it. A URL without any leaves the list as it is, so
following a plain link to
/postsdoes not reset a filtered list. - The params are also saved in the store under
<resource>.listParams. When the URL carries no list params, the list restores the saved ones, then falls back to the options (perPage,sort,filterDefaultValues). PassstoreKey: "other"to namespace them, orstoreKey: falseto turn the persistence off.
Tips
- A
<List>must be inside a<ResourceContextProvider>(a<Resource>supplies one) or be given an explicitresourceprop, or it throws. - The children are usually a
<DataTable>and a<ListPagination>, but any component that reads the list context works. Build your own layout on top ofuseListContext. perPagesets the initial page size. A page size the user picked, or one in the URL, wins over it.