Ga naar hoofdinhoud

CnDataTable

Sortable data table with row selection, loading states, and schema-driven column generation. Supports dot notation for nested values (e.g., address.city).

Wraps: NcLoadingIcon, NcCheckboxRadioSwitch, CnCellRenderer

Try it

Loading CnDataTable playground…

CnDataTable showing sortable columns, checkboxes, and row action buttons

Anatomy

+--+------+----------↑---------+----------+----------+---------+----------------+
| | sel. | Column A ▲ | Column B | Column C | Column D| Actions header |
+--+------+--------------------+----------+----------+---------+----------------+
|☐ | 👤 | Alice van den Berg | Dept | email@.. | Active |⋮ |
|☐ | 👤 | Bob Jansen | Dept | email@.. | Pending |⋮ |
|☐ | 👤 | Carol Smit | Dept | email@.. | Active |⋮ |
+--+------+--------------------+----------+----------+---------+----------------+
↑ ↑ ↑ ↑ ↑
| avatar cell value badge row actions
checkbox renderer
RegionDescription
Select-all checkboxChecks/unchecks all rows on the current page
Column headersClickable to sort; cycles through ascending (▲), descending (▼), and no sort (indicator hidden)
Avatar / iconAuto-generated from the row's name field via CnCellRenderer
Cell valueType-aware rendering: email links, dates, booleans, status badges
Row actionsPer-row menu — rendered via the #row-actions slot
Actions headerSlot above actions row — rendered via the #actions-header slot (only renders when row actions exist)
Loading overlaySpinner centered over the table body while loading is true
Empty state"No items found" message (or #empty slot) when rows array is empty

Usage

<CnDataTable
:schema="schema"
:rows="objects"
:sort-key="sortKey"
:sort-order="sortOrder"
:selectable="true"
:selected-ids="selected"
@sort="onSort"
@select="onSelect"
@row-click="onRowClick">
<template #row-actions="{ row }">
<CnRowActions :actions="rowActions" :row="row" @action="onAction" />
</template>
</CnDataTable>

Props

PropTypeDefaultDescription
schemaObjectnullSchema object for auto-generating columns from its properties map
columnsArray[]Column definitions: [{ key, label, sortable?, width?, align?, class?, cellClass?, formatter?, widget?, widgetProps?, aggregate? }]. formatter/widget/widgetProps resolve against the app's cnFormatters/cnCellWidgets registries (provided by CnAppRoot) — see migrating-to-manifest → Column formatters / Column widgets. aggregate ({ register?, schema, op:"count", where }) renders the cell as a count of related OpenRegister objects (one _limit=0 request per row; @self.<path> in where interpolated per-row; while loading, on failure) — see migrating-to-manifest → Aggregate columns. The #column-{key} scoped slot still overrides everything.
rowIconString | FunctionnullOptional leading icon at the start of every row. A static MDI icon name (PascalCase, e.g. 'FileDocumentOutline') applied to all rows, or (row) => iconName to vary it per row. Resolved through the shared CnIcon registry. Unset = no icon column.
columnOverridesObject{}Per-column overrides applied on top of schema-generated columns; keyed by column key
excludeColumnsArray[]Column keys to hide when using schema auto-generation
includeColumnsArraynullWhitelist of column keys to show; all others hidden (takes precedence over excludeColumns)
rowsArray[]Array of row data objects to display
loadingBooleanfalseShows a loading spinner overlay while true
loadingTextString'Loading...'Accessible label for the loading spinner
sortKeyStringnullCurrently sorted column key; controls the ▲/▼ indicator. null means no column is actively sorted.
sortOrderString'asc'Current sort direction — 'asc', 'desc', or null (no sort)
selectableBooleanfalseEnables the checkbox column for multi-row selection
rowClickToViewBooleanfalseWhen true, a row-body click emits row-click (for navigation) even while selectable — selection then happens only via the checkbox column ("click row = open, tick box = select")
selectedIdsArray[]Array of currently selected row IDs (controlled)
rowKeyString'id'Property name used as the unique row identifier
emptyTextString'No items found'Message shown when rows is empty and no #empty slot is provided
rowClassFunctionnullCallback (row) => cssClass to add dynamic CSS classes to rows
cellClassFunctionnullCallback (row, col) => cssClass to add dynamic CSS classes to individual data cells
scrollableBooleanfalseEnables horizontal scrolling for wide tables
selectAllLabelString'Select all rows'Accessible name (aria-label) for the select-all checkbox in the header row, so screen readers announce a named control (WCAG 4.1.2)
selectRowLabelString'Select row'Accessible name (aria-label) for each per-row select checkbox, so screen readers announce a named control (WCAG 4.1.2)

Events

EventPayloadDescription
sort{ key, order }Emitted when a sortable column header is clicked. Cycles through asc → desc → null. When the user clears the sort, both key and order are null.
selectids[]Emitted when row selection changes; payload is the full updated selection array
select-allisSelectAllEmitted when the select-all checkbox is toggled
row-clickrowEmitted when a data row is clicked (not the checkbox). Only fires when selectable is false — when selectable is true, a deliberate click anywhere on a row toggles its selection (emitting select) instead — a text-selection drag is not treated as a click.
row-context-menu{ row, event }Emitted when a data row is right-clicked. The native contextmenu event is prevented. Used by CnIndexPage with the useContextMenu composable to show a context menu at the cursor position.

Slots

SlotScopeDescription
#column-{key}{ row, value }Override the cell renderer for a specific column key
#row-actions{ row }Content for the last (actions) cell of each row — typically CnRowActions
#actions-header-Content for the header above the actions cell — typically a button
#emptyCustom empty-state content shown when rows is empty

Reference (auto-generated)

The tables below are generated from the SFC source via vue-docgen-cli. They reflect what's actually in CnDataTable.vue and update automatically whenever the component changes.

Props

NameTypeRequiredDefaultDescription
columnsArray<{key: string, label: string, sortable: boolean, width: string, class: string, cellClass: string}&#124;string>[]Column definitions (manual mode). Not required when schema is provided. Each entry may be a full column object OR a bare string key; bare strings are normalised to { key, label } by effectiveColumns so manifest-driven pages that pass config.columns as a string array work without extra mapping.
rowIconstring &#124; ((row: object) => string) &#124; nullnullOptional leading icon shown at the start of every row. Either a static MDI icon name (PascalCase, e.g. 'FileDocumentOutline') applied to all rows, or a function (row) =&gt; iconName to vary it per row. The icon is resolved through the shared CnIcon registry. Unset = no icon column.
schemaobjectnullSchema object with properties field (schema-driven mode). When provided, columns are auto-generated from schema properties.
columnOverridesobject\{\}Per-column overrides when using schema mode: { key: { width, label, sortable, ... } }
excludeColumnsarray[]Column keys to exclude when using schema mode
includeColumnsarraynullColumn keys to include when using schema mode (whitelist)
rowsarray[]Row data array. Each row should have a unique identifier (see rowKey).
loadingbooleanfalseWhether data is loading (shows loading spinner)
sortKeystringnullCurrent sort column key
sortOrderstring'asc'Current sort order: 'asc', 'desc', or null (no sort)
selectablebooleanfalseWhether rows can be selected with checkboxes
selectedIdsarray[]Array of currently selected row IDs
rowKeystring'id'Property name used as unique row identifier
emptyTextstring() =&gt; t('nextcloud-vue', 'No items found')Text shown when there are no rows
rowClassfuncnullFunction returning CSS class(es) for a row: (row) => string|object
cellClassfuncnullFunction returning CSS class(es) for a data cell: (row, col) => string|object
scrollablebooleanfalseWhether to constrain table height and make it scrollable
loadingTextstring() =&gt; t('nextcloud-vue', 'Loading...')Text shown while loading
selectAllLabelstring() =&gt; t('nextcloud-vue', 'Select all rows')Accessible name for the select-all checkbox in the header row. Used as the checkbox's aria-label so screen readers announce a named control (WCAG 4.1.2). Defaults to the lib's translation of "Select all rows".
selectRowLabelstring() =&gt; t('nextcloud-vue', 'Select row')Accessible name for a per-row select checkbox. Used as the checkbox's aria-label so screen readers announce a named control (WCAG 4.1.2). Defaults to the lib's translation of "Select row".
titlestring''Optional card title rendered in a header above the table. When set, the table reads as a self-contained card (the container's own border/radius is the card chrome). Folded in from the retired CnTableWidget.
borderlessbooleanfalseDrop the container's card chrome (border, radius, shadow) so the table sits flush inside a parent that already provides a card (e.g. a CnWidgetWrapper dashboard slot). Folded in from CnTableWidget.
limitnumber0Max number of rows to display. When the total exceeds it, only the first limit render and the "View all" footer appears (with viewAllRoute). 0 = show all. Folded in from CnTableWidget.
viewAllRouteunionnullvue-router route object for the "View all" footer link. The footer only shows when set AND the rows are a limit-ed subset. Folded in from CnTableWidget.
viewAllLabelstring() =&gt; t('nextcloud-vue', 'View all')Pre-translated "View all" footer label.
registerunionnullSelf-fetch mode (folded from CnTableWidget): the OpenRegister register id/slug. When register + schemaId are set and no rows are passed, the table fetches /apps/openregister/api/objects/{register}/{schemaId}.
schemaIdunionnullSelf-fetch mode (folded from CnTableWidget): the OpenRegister schema id used together with register. (Distinct from the schema prop, which is a JSON Schema object for column generation.)
rowClickRouteunionnullConvenience navigation (folded from CnTableWidget): a function that receives the clicked row and returns a vue-router route to push. When set, a row click navigates there (the row-click event still fires).
rowClickToViewbooleanfalseWhen true, a row-body click emits row-click (for navigation) even while selectable — selection then happens only via the checkbox column. Lets "click row = open, tick box = select" coexist. Default false keeps the legacy behaviour (selectable rows select on body click).

Events

NamePayloadDescription
row-context-menu
row-clickEmitted on a row-body click for navigation. Fires when selectable is false, OR when rowClickToView is set (selection then happens via the checkbox column).
sortEmitted when a sortable column header is clicked.
selectEmitted when row selection changes. Payload: array of selected IDs.
select-allEmitted when select-all checkbox is toggled.

Slots

NameBindingsDescription
actions-header
empty
'column-' + col.keyname, row, value
row-actionsrow

Card / widget mode (folded from CnTableWidget)

CnDataTable is now the single table component — the deprecated CnTableWidget's features are folded in here as opt-in props (bare-table usage is unchanged):

  • title — render a card header (title + total-count badge) above the table.
  • borderless — drop the container's card chrome so the table sits flush inside a parent card (e.g. a CnWidgetWrapper dashboard slot).
  • limit — show only the first N rows; with viewAllRoute a "View all" footer appears.
  • viewAllRoute / viewAllLabel — the footer link's route and label.
  • register + schemaId — self-fetch rows from OpenRegister when no rows are passed.
  • rowClickRoute — a function mapping a clicked row to a vue-router route to push.