CollectionListView
CollectionListView<
TCollectionSlug,TDoc>(props):Element
Defined in: packages/react/src/collections/listView/CollectionListView/index.tsx:140
Collection list view component.
Renders a data table of all documents in a collection. The shell (title, header actions,
view tabs, toolbar, table frame) never reads row data and renders immediately from
config/URL/cookies — only the table body, footer, and bulk-selection count come from a
live query, isolated inside CollectionListViewData’s own <Suspense> boundary
(developer-binding redesign: TanStack Query v5 + Next.js App Router streaming —
NextAdminPage prefetches the identical convexQuery(...) key via prefetchQuery +
dehydrate/HydrationBoundary, without awaiting it, so the shell streams first and the
row data streams in behind it). Search, sort, filter, and page size all round-trip
through the URL (Decision 9).
This component renders the content area only — wrap it in AdminLayout.
Owns the shared list state (collection, URL state, selection, density,
visible columns, onInspect) and mounts ListViewContextProvider around
ListViewLayout (Decision 37), which delegates to ListViewLayout for the
actual composition of the seven list.* slots and the toolbar.
RevalidateButton renders in the SAME header row as the title and
list.header.actions (Decision 23 — owned by CollectionListView
itself, never a slot contribution), passed through ListViewLayout’s
headerExtra prop rather than as a sibling block, so the header never
splits onto two lines.
Density (Decision 29) is seeded from props.initialPreferences?.density
(resolved server-side from the consolidated vex-admin cookie, Decision 51) —
never from useLocalStorage, which would make the client’s first paint
depend on an effect. onDensityChange both updates local state and
writes the cookie, so the next server render agrees before any client JS
runs.
liveRows/isSearchFetching are lifted state: CollectionListViewData
(the Suspense-wrapped inner component) reports the resolved page and
fetch status up via onRowsChange/onSearchFetchingChange so
ListViewContextProvider’s selection.selectedDocs/isSearchFetching —
read by things OUTSIDE the Suspense boundary (the bulk-actions bar,
ListViewSearch’s spinner) — stay correct without those consumers needing to
live inside the boundary themselves. Both start empty/false — nothing
can be selected or mid-search before the first page exists anyway, so
this is a no-op during the (brief) streaming window, never a
user-visible gap.
Type Parameters
Section titled “Type Parameters”TCollectionSlug
Section titled “TCollectionSlug”TCollectionSlug extends string = string
TDoc extends TDocument = TDocument
Parameters
Section titled “Parameters”CollectionListViewProps<TCollectionSlug>
View props.
Returns
Section titled “Returns”Element
The collection data table, or a not-found message when
collection does not resolve against the current config.
Throws
Section titled “Throws”Never — resolution failure renders a not-found message instead of throwing.
Example
Section titled “Example”<CollectionListView collection="posts" />On a versioned collection (collection.versions.drafts), requests
drafts: true (gated on readDrafts) so an in-progress edit’s draft
row is visible. find/search themselves page over one “canonical”
row per logical document and merge each linked draft’s fields back in,
server-side, per page (collections/mergeDraftCounterparts.ts,
@vexcms/core) — pagination.results already carries one row per
logical document, never a draft row split from its published
counterpart. The public read path is unaffected: it never requests
drafts and never sees a draft row, paired or standalone.