Quickback Docs

Table Views

Browse tables in Table mode or edit inline with the spreadsheet-style Data Table mode.

Table Views

The CMS provides two view modes for every table: Table mode for browsing and navigating records, and Data Table mode for spreadsheet-style inline editing. Switch between them with the toolbar toggle.

Table Mode

Table mode is the default browse experience:

  • Click rows to navigate to the record detail view
  • Row action menus (three-dot icon) with view, edit, delete, and custom actions
  • Column headers are sortable — click to toggle ascending/descending
  • Search bar for full-text search across fields allowed by the active collection or named-view query contract
  • Responsive columns that adapt to available space

Each row shows the most relevant columns for the table. Foreign key columns display human-readable labels (resolved via _label fields) instead of raw UUIDs. Boolean columns render as colored Yes/No badges. Enum columns use color-coded pills.

Row Actions

The three-dot menu on each row provides:

ActionVisibilityDescription
View detailsAlwaysNavigate to the record detail page
EditWhen role has update accessNavigate to the edit form
Custom actionsFiltered by role + record stateRun actions like approve, void, post
DeleteWhen role has delete accessDelete with confirmation

Actions are filtered by the current role's write permissions and the action's access conditions. For example, an "approve" action that requires status === "pending" only appears on pending records.

Data Table Mode

Data Table mode provides an Excel/Google Sheets-like editing experience:

  • Cell selection with a blue focus ring
  • Keyboard navigation using arrow keys, Tab, Enter, and Escape
  • Type-to-edit — start typing to enter edit mode on the selected cell
  • Row numbers displayed in the leftmost column
  • Hint bar at the bottom showing keyboard shortcuts

Editable Fields

Which cells are editable is determined by the table's guards:

  • Fields in guards.updatable are editable
  • Fields in guards.immutable are read-only (shown with a disabled style)
  • Fields in guards.protected are read-only (marked "Updated via actions only")
  • Audit fields (createdAt, createdBy, modifiedAt, modifiedBy) are always read-only

Non-editable cells can still be selected and copied, but they don't enter edit mode.

Cell Types

Column TypeEdit ControlBehavior
TextText inputFree-text entry
NumberNumber inputNumeric entry with step controls
BooleanYes/No dropdownToggle between Yes and No
FK referenceTypeahead dropdownServer-side search with debounced queries
EnumSelect dropdownChoose from allowed values

Saving

Changes are auto-saved when you:

  • Press Enter to confirm and move down
  • Press Tab to confirm and move to the next editable cell
  • Click away from the editing cell (blur)

Press Escape to cancel an edit and revert to the previous value.

Views

Views are named column projections defined in your table's resource config. They control which columns appear in the table based on the current role.

Toolbar

The toolbar shows a view dropdown when the table has named views. Its options are the named views the current role may access.

For a table with no named views, the toolbar renders no view control and the table uses its ordinary collection projection. Declaring any read.views replaces that projection with the named-view contract; All Fields is not an extra option alongside the named views. Set read.defaultView to choose which named projection opens first in the CMS. The schema registry carries that selection as defaultView, and the CMS calls the named-view route from its first request.

If named views exist without read.defaultView, the registry emits requiresExplicitView: true. The CMS shows Choose a view and makes no data request until the user selects an accessible named view. It never silently chooses the first accessible projection, because projection choice is part of the table's data/access contract.

Views are filtered by role access. If a view's access rules require admin and the current user is a member, that view won't appear in the dropdown.

Example

Given this definition:

read: {
  defaultView: 'summary',
  views: {
    summary: {
      fields: ['id', 'name', 'status'],
      access: { roles: ['member+'] },
    },
    full: {
      fields: ['id', 'name', 'status', 'ssn', 'internalNotes'],
      access: { roles: ['admin+'] },
    },
  },
}
  • Members see summary, which opens by default.
  • Admins and owners see summary and full; summary still opens by default.

When a view is selected, the CMS calls GET /api/v1/<table>/views/<name>, fetching only the projected columns. With named views but no read.defaultView, bare GET /api/v1/<table> returns 400 VIEW_REQUIRED. That can be intentional for API callers that must choose a projection, but a CMS table intended to open directly should declare an accessible default view.

Views Used by FK Lookups

When this table is the target of an FK selector, a table with no named views uses its ordinary collection route. A table with named views is different: the CMS lookup uses only the authored read.defaultView, verifies that the current role may access it, and calls its named-view route. It never retries the bare collection endpoint and never chooses another named view automatically.

Make that default view lookup-safe by including the table's primary key and displayColumn in fields. For searchable label typeahead, include the displayColumn in the view's effective query.searchable allowlist. An explicit named-view allowlist is authoritative, so the column does not also need .searchable(). The schema registry emits the effective inherited view access and searchability so the CMS can decide before requesting data.

If the default is missing, inaccessible, or omits the primary key/label, the selector is disabled with an actionable message. If the default is otherwise valid but its effective searchable list is empty, the selector shows the first 50 options as a chooser, keeps search read-only, and sends no ?search.

Default Sort

You can set a default sort order for a table by adding defaultSort to your resource config:

export default defineTable(podcastEpisodes, {
  defaultSort: { field: "createdAt", order: "desc" },
  // ...
});

When the CMS loads the table, it applies this sort automatically. Users can still click column headers to change the sort — defaultSort only sets the initial state.

defaultSort does not select a named view. Adding or changing it cannot resolve VIEW_REQUIRED; use read.defaultView (or call the explicit named-view route) for that.

Pagination

The bottom of every table view shows pagination controls:

  • Page range indicator — "Showing 1-25 of 142"
  • Page buttons — Navigate directly to a page, with ellipsis for large page counts
  • Previous/Next arrows

The default page size is 25 records. The CMS resets to page 1 when you change the search query or switch views.

The search bar performs full-text search across the fields allowed by the active collection or named-view query contract. Type a query and the CMS debounces the request, then fetches matching records from the API. For named views, the registry's effective query.searchable list tells the CMS whether the route accepts ?search and which projected fields participate.

Search works in both Table and Data Table modes when the active route supports it. For FK selectors, an empty named-default-view search allowlist activates the first-50 chooser behavior above, which sends no ?search.

Next Steps

  • Inline Editing — Deep dive into spreadsheet editing, FK typeahead, and keyboard shortcuts
  • Security — How roles and guards affect the table UI
  • Actions — Custom actions in the row menu and detail view

On this page