Record View

Record View is the record workflow's table and detail surface, and the family the Pro tier is built around.

Installation

Nothing to add. This component ships as source in your download, so it is already in your project.

Filesrc/vui/record-view.tsx
Import from@viliha/vui-react/record-view

The exported names are at the top of that file. The import path is an alias the download's tsconfig.json points at src/vui/, so you can move that folder and change one line rather than every file that imports from it.

Usage

record-view-demo.tsx
import {
  RecordView,
  PageChromeProvider,
  MissingValue,
  RecordForm,
  RecordFormPanel,
} from "@viliha/vui-react/record-view";

Exports

Everything this family exports. They are read from the source, so this list is what the package actually ships.

PropTypeDefault
RecordViewcomponent—
PageChromeProvidercomponent—
MissingValuecomponent—
RecordFormcomponent—
RecordFormPanelcomponent—

RecordView props

PropTypeDefaultWhat it does
titlestring—
singularstring—
icon?IconType—
fieldsRecordField<T>[]—
initialData?T[]—Seed rows for a client-managed table. Optional — omit in `fetcher`/`manual` mode (the server owns the data) or for a read-only list. Defaults to `[]`.
makeEmptyRow?() => T—Factory for a blank row, used by the Add action. Omit it (and `onCreate`) for a read-only list — the "+ New" button is then hidden.
avatar?string; }—A photograph for the row.
addAriaLabel?string—Override the add button's accessible name. Defaults to `Add {singular}`.
bare?booleanfalseDrop the frames this component draws for itself. For a caller that has already drawn a card, a title band and its padding.
formMode?"panel" | "page""panel"Add/Edit form presentation: "panel" slide-over (default) or "page" full-page.
formColumns?1 | 21Full-page form column count (page mode only). Default 1.
onHome?() => void—Navigate to Home from the page-form breadcrumb (e.g. router.push).
formDescription?string—Intro text for the page-form documentation panel ("about this form").
data?T[]—Controlled rows. When set, RecordView renders these and reports edits via onDataChange instead of holding rows in internal state.
onDataChange?(rows: T[]) => void | Promise<void>—Receives the next rows array after an add, edit, delete or restore.
onCreate?() => void—When set, the "add" button calls this (e.g. navigate to a create route) instead of opening the built-in form.
onView?(id: RowId) => void—When set, opening/editing a row navigates (e.g. to an edit route) instead of opening the built-in overlay form.
onEdit?(id: RowId) => void—
onFormOpen?(mode: "create" | "edit" | "view", row?: T) => void—Notified whenever the Add / View / Edit form opens, so you can lazily load field data (e.g. FK/combobox option catalogs) only when a user actually opens a form — not on every table mount.
persistKey?string—Persist this view's filter / sort / page under this key (e.g. the route), so the work survives leaving and returning via the open-tabs strip.
resizableColumns?boolean—Allow dragging a column's right edge to resize it. Defaults to `NEXT_PUBLIC_RESIZABLE_COLUMNS` (on unless set to `0`/`false`), so a long value in a narrow column is always reachable.
onFilter?(values: FilterValues<T>) => void—Called from the Filter panel's Search (and Clear) when fields are `filterable`. Receives the collected per-field values; run your own query or client-side filtering here.
loading?booleanfalseShow animated skeleton rows instead of the table body while data loads from the server (an initial fetch or a filter/refetch). Set it around your async load; the toolbar stays usable.
manual?booleanfalseServer-side mode.
rowCount?number—Total row count on the server — drives the pagination footer and page count in `manual` mode (RecordView can't infer it from a single page of `data`).
onQueryChange?(query: ServerQuery<T>) => void—Server mode: called with the full query whenever page, page size, sort, or the keyword changes (and on the per-field Filter Search/Clear). Fetch and update `data` + `rowCount` + `loading` in response.
fetcher?(query: ServerQuery<T>, signal: AbortSignal) => Promise<{ rows: T[]; total: number }>—Server data source.
cacheKey?string—Namespaces the `fetcher` response cache (like `persistKey`). Responses are cached per query and survive remounts / tab switches, so returning to a tab is instant with no refetch.
cache?false | { max?: number; ttlMs?: number }—`fetcher` cache tuning, or `false` to never cache a page.
onError?(error: unknown, query: ServerQuery<T>) => void—Called when a `fetcher` request rejects (non-abort). RecordView keeps the previously loaded data and clears the loading state.
maxCellChars?number—Max characters any table cell shows before truncating to one line with an ellipsis + hover tooltip (long text never wraps). Defaults to `NEXT_PUBLIC_MAX_CELL_CHARS` (or 25).
defaultPageSize?number—Initial rows per page. Defaults to `NEXT_PUBLIC_DEFAULT_PAGE_SIZE` (or 25), clamped to `maxPageSize`.
maxPageSize?number—Ceiling for the page-size selector (options above it are hidden). Defaults to `NEXT_PUBLIC_MAX_PAGE_SIZE` (or unbounded).
nameLabel?string"Name"Header for the leading identity column. Default "Name" — set e.g. "Title" for tables whose identity is a title field (regions, roles, …).
nameSortKey?Extract<keyof T—Field key the identity column sorts by, so its header toggles sort + shows a caret like other columns. Defaults to the first `hideInTable` field marked `sortable` (the field that drives `getPrimary`).
identityColumn?number | "first" | "last" | "hidden""first"Where the leading identity (Name/Title) column sits among the field columns.
showImport?booleantrueShow the Import (CSV/JSON/Excel) menu. Default `true`.
importActions?IoActionsConfig<T>—What the Import menu offers. An array replaces the shipped entries, a function receives them so you can add to them. Point an `onAct` at your API to upload the file and let the server do the work.
exportActions?IoActionsConfig<T>—What the Export menu offers, same shape. Use `ctx.query` to ask your API for everything that matches rather than the page on screen.
showExport?booleantrueShow the Export (CSV/Excel/JSON/PDF) menu. Default `true`.
showAdd?booleantrueShow the "+ {singular}" add button (still also requires `onCreate` or `makeEmptyRow`). Default `true`.
formRows?FormRow[]—The add/edit form's rows: which sections sit side by side on each one.
sectionColumns?SectionColumns—number of sections instead of one count for the whole form.
sections?FormSection[]—Section metadata (order, description) when you aren't declaring rows.
behaviour?BehaviourConfig—Behaviour overrides for this table only: what a row click does, whether delete confirms, how long the saved-row highlight lasts, and so on.
formActions?FormActionsConfig<T>—Footer buttons for the add/edit/view form.
renderFooter?(ctx: FormActionContext<T>) => React.ReactNode—Replace the form footer outright. The array covers almost everything, so reach for this only when it genuinely can't express what you need.
formSlots?FormSlot<T>[]—Your own content between the form's fields — a callout, a preview, a pair of custom controls. Each slot renders as a full-width row inside its section, so it inherits the card, separators and padding.
showEdit?boolean—Show the row Edit (pencil) action and the Edit button on the view panel.
showFilter?booleantrueShow the Filter panel. Default `true`.
filterExtras?React.ReactNode—Extra rows to add to the Filter panel.
showSort?booleantrueShow the Sort menu. Default `true`.
showPagination?booleantrueShow the pagination footer. When `false` in client mode, all rows render (no page slicing). Default `true`.
showSelection?booleantrueShow row selection — the checkbox column, bulk Actions, and Clear selection. `false` also removes drag-to-reorder (it shares the leading column). Default `true`.
showTrash?booleanfalseShow a **Trash** toggle in the header (left of the Filter control). Off by default. Enabling it lets RecordView switch the SAME table between live and soft-deleted rows.
trashedData?T[]—Soft-deleted rows shown while Trash is active in **client mode** (`data` + `onDataChange`). Omit in `manual`/`fetcher` mode — there the host returns trashed rows for the `trash: true` query instead.
onRestore?(rows: T[]) => void | Promise<void>—Restore rows from Trash — one row (its Restore icon) or the current selection (bulk "Restore N selected"), after a confirm.

Customize

Every value this component draws comes from the theme: the colours, the radius, the control height and the type scale. Change a token and this moves with everything else, which is the difference between theming the product and overriding a component. See Theming for the token contract, and Swapping defaults for replacing the markup itself.