Installation
Nothing to add. This component ships as source in your download, so it is already in your project.
src/vui/record-view.tsx@viliha/vui-react/record-viewThe 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
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.
| Prop | Type | Default |
|---|---|---|
| RecordView | component | — |
| PageChromeProvider | component | — |
| MissingValue | component | — |
| RecordForm | component | — |
| RecordFormPanel | component | — |
RecordView props
| Prop | Type | Default | What it does |
|---|---|---|---|
| title | string | — | |
| singular | string | — | |
| icon? | IconType | — | |
| fields | RecordField<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? | boolean | false | Drop 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 | 2 | 1 | Full-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? | boolean | false | Show 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? | boolean | false | Server-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? | boolean | true | Show 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? | boolean | true | Show the Export (CSV/Excel/JSON/PDF) menu. Default `true`. |
| showAdd? | boolean | true | Show 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? | boolean | true | Show the Filter panel. Default `true`. |
| filterExtras? | React.ReactNode | — | Extra rows to add to the Filter panel. |
| showSort? | boolean | true | Show the Sort menu. Default `true`. |
| showPagination? | boolean | true | Show the pagination footer. When `false` in client mode, all rows render (no page slicing). Default `true`. |
| showSelection? | boolean | true | Show row selection — the checkbox column, bulk Actions, and Clear selection. `false` also removes drag-to-reorder (it shares the leading column). Default `true`. |
| showTrash? | boolean | false | Show 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.