Skip to content

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.

TCollectionSlug extends string = string

TDoc extends TDocument = TDocument

CollectionListViewProps<TCollectionSlug>

View props.

Element

The collection data table, or a not-found message when collection does not resolve against the current config.

Never — resolution failure renders a not-found message instead of throwing.

<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.