When to Use the Table Block
Show a list of records users can scan, sort, filter and page through.
Let users act on records via per-row buttons or bulk actions over many selected rows.
Let users edit records in place, with edits batched into a single save.
Give users saved views, grouping or a summary footer over the data.
Note: Table vs Repeatable: Use a Repeatable when every record should render as its own free-form card of blocks. Use a Table when records belong in columns and users need sorting, filtering, pagination, selection or inline editing.
Data Binding
A table's rows come from one of two Source modes, chosen under Inspector → Content → Source.
| Source Mode | How rows are supplied | What the table does for you |
Object | Pick a platform object under Select Object; the table queries it automatically | Applies server-side filters & sorts; computes total record count and pagination totals |
Mapped | Bind your own data (API result, variable, another block) under Data Source | Nothing — you own the query, pagination offset/size, and must supply Total Records yourself |
Primary Key
The Primary Key field uniquely identifies each row. It is required for row selection, inline editing, drag-reordering, and saved views to work correctly. Set it under Inspector → Content → Primary Key.
Warning: Changing the data source rebuilds columns. Selecting or changing the data source regenerates the column list from the response and replaces any custom configuration — column names, formats, widths and editability you set up are lost. Only text, number, and boolean fields from the response become columns automatically. Reconfigure columns after switching sources.
Content Properties
| Property | Type | Req? | Where to Set | Description |
source | "Object" | "Mapped" | Optional | Inspector → Content → Source | Whether rows come from a platform object or from manually bound data |
entityId | object | Conditional | Inspector → Content → Select Object | The platform object whose records fill the table. Visible when Source = Object |
dataSourceId | data, bindable | Conditional | Inspector → Content → Data Source | The data bound as the table's rows. Visible when Source = Mapped |
identifier | field key | Required | Inspector → Content → Primary Key | The field that uniquely identifies each row. Required for selection and inline editing |
columns | column list | Optional | Inspector → Content → Columns | The table's columns and their types — see Column Types |
rowSelection | "None" | "Single" | "Multiple" | Optional | Inspector → Content → Row Selection | Whether users can select rows — see Row Selection |
rowActions | action list | Optional | Inspector → Content → Row Actions | Per-row buttons rendered in the actions column |
bulkActions | action list | Optional | Inspector → Content → Bulk Actions | Actions over selected rows; requires Row Selection = Multiple |
Appearance Properties
| Property | Type | Default | Description |
gridLineAxis | 7 options | — | Which borders the grid draws: Both, Both Between, Horizontal, Horizontal Between, Vertical, Vertical Between, or None |
rowHeight | "Compact" | "Comfortable" | "Spacious" | — | Row density: 36px (Compact), 52px (Comfortable), 92px (Spacious) |
hideHeaders | boolean | false | Hides the header row entirely |
wrapColumnHeader | boolean | false | Wraps long header labels instead of truncating. Visible when Hide Header is off |
emptyMessage | string | "No data found" | Text shown when the table has no rows and no filter is active |
rowHoverColor | color, condition-capable | — | Row background while hovered; can vary per row by condition |
Note: Empty State slot vs Empty Message: A block in the Empty State slot replaces both the Empty Message and the "All records are filtered" message. If a search or filter is active with zero results, the fixed message "All records are filtered" shows instead of your custom Empty Message — only the slot overrides it.
Add-ons
Add-ons live under Inspector → Content → Add-ons. Each opens its own detail panel.
| Add-on | Default | What it adds |
| Toolbar | Enabled, title "Table Title" | Title, record count, filter, search, sort, column visibility, views — see Toolbar & Views |
| Pagination | Page based, 30/page | Page or scroll-based paging |
| Row Ordering | Enabled | Drag-to-reorder rows; produces a rowOrderChangesetList |
| Column Ordering | Enabled | Drag-to-reorder columns |
| Column Resizing | Enabled | Drag column borders to resize |
| Sub Rows | Off | Expandable nested child rows — see Row Selection & Expansion |
| Detail Panel | Off | Expanding a row renders a block slot beneath it. Mutually exclusive with Sub Rows |
| User State Settings | Remember on | Persists the user's paging, filters and column layout between visits |
| Save Action | Off | The Save/Cancel bar for inline edits — appears only once a column is editable |
| Summary | Off | A summary footer row — appears only once a column has a summary configured |
Pagination
| Property | Type | Default | Description |
type | "Page based" | "Scroll based" | Page based | Numbered pages or infinite scroll |
size | number | 30 | Rows per page |
pageSizeMode | "Fixed" | "User Selectable" | Fixed | Whether users can change the page size. Page based only |
total | number, bindable | — | Total row count. Required for Mapped sources; auto-computed for Object sources |
initialPage | number | 1 | Which page loads first. Page based only |
Warning: Mapped sources need Total Records. With a Mapped source and page-based pagination, the table cannot know the total row count — bind Total Records yourself or the page navigation has no total to count against.
Events
Configure these under Inspector → Content → Interactions (On Save under the Save Action add-on).
| Event | Trigger | Fires when |
| On Select Row | A row is selected | Always |
| On Change Row Selection | A row's checkbox is toggled | Multiple selection only |
| On Click Cell | A cell is clicked | Always |
| On Change Cell Value | An editable cell's value changes | Editable columns |
| On Change Row Order | A row is dragged to a new position | Row Ordering enabled |
| On Page Change | The user moves to another page | Page-based pagination |
| On Receive Notification | A notification targets the table | Always |
| On Save | User clicks Save on the changeset bar | Save Action add-on configured |
Note: Clicking a row action also sets {{ id.selectedRow }} and {{ id.selectedRowKey }} before the action's own event runs, so the handler and anything bound to selection always sees the clicked row.
Methods
Wire any event to a Trigger Component Method action, pick the table under Block ID, the method under Method Name, and fill Method Payload when required. Methods only run while the table is visible — calling a method on a hidden table does nothing and the call is not queued.
| Method | Params | Available when | What it does |
refetchData | — | Always | Re-runs the table's data query |
exportData | fileName, fileType, sheetName, exportHiddenColumns | Always | Exports to CSV, XLS, or XLSX |
clearRowSelection | — | Always | Clears the current row selection |
selectRow | rowId | Single selection | Selects one row by primary key |
selectRows | rowIds | Multiple selection | Selects rows by primary key |
selectAllRows | — | Multiple selection | Selects every row on the current page |
clearChangeset | changesetCursor (optional) | Editable columns | Discards pending inline edits (all, or up to cursor position) |
expandAllRows / collapseAllRows | — | Sub Rows enabled | Expands or collapses every parent row |
applyKeywordSearch / clearKeywordSearch | searchText (apply only) | Search action configured | Set or clear the search term programmatically |
Exposed State
Read these from anywhere on the page as {{ id.* }} where id is the table's block ID.
| State key | Type | Description |
{{ id.selectedRow }} | object | Data of the last selected row (Single selection) |
{{ id.selectedRowKey }} | string | Primary key of the last selected row (Single selection) |
{{ id.selectedRows }} | array | Data of all selected rows (Multiple selection) |
{{ id.selectedRowKeys }} | array | Primary keys of all selected rows (Multiple selection) |
{{ id.currentData }} | array | Rows currently rendered, with pending edits already applied |
{{ id.filters }} | object | Current search, sort and filter state |
{{ id.changesetList }} | array | Pending inline edits as a list ready for an update call |
{{ id.rowOrderChangesetList }} | array | Pending drag-reorder changes |
{{ id.activeView }} | object | Active saved view: id, name, isDirty |
{{ id.views }} | array | Available saved views |
{{ id.content.page.size }} | number | Current page size |
{{ id.content.page.offset }} | number | Index of the first row on the current page |
{{ id.context.currentRow }} | object | The row being rendered — only valid inside the table |
{{ id.context.currentRowIndex }} | number | The row's position (0-indexed) — only valid inside the table |
Warning: context bindings (currentRow, currentRowIndex) only resolve inside the table — in a row action's settings, a cell value, or a block placed in a custom cell. From a button elsewhere on the page, use {{ id.selectedRow }} instead.
Common Patterns
Behavior & Gotchas
Bulk actions lock selection to Multiple
While any bulk action is configured, the Single and None selection modes are disabled in the inspector. Remove the bulk actions first if you need Single or None.
No built-in way to add or delete rows
The table has no add-row or delete-row control and no method to insert or remove a row. With a Mapped source, change the bound data itself. With an Object source, run a create/delete call from a button or row action, then call refetchData.
The header stays put; the grid scrolls
The header row is sticky by default. The grid scrolls vertically inside a capped container while the header stays visible. Horizontal scrolling appears automatically when columns are wider than the block. Turn on a column's Grow setting so columns share leftover width instead of overflowing.
Methods do nothing on hidden tables
Calling any method on a hidden table (behind a visibility condition, on an inactive tab, inside a closed drawer) silently does nothing — the call is not queued. Trigger methods after the table is shown, not before.
User State Settings remembers layout and paging, not selection
With User State Settings on (the default), each user's page number, filters, sort, search, column order and widths are persisted. Row selection and pending edits are not saved. Turn on Reset When Hidden to clear state on hide.
Frequently Asked Questions
What is the difference between an Object source and a Mapped source?
An Object source points the table at a platform object (a stored business entity with records): the table runs the query, applies toolbar filter/search/sort server-side, and computes pagination totals for you. A Mapped source means you bind the rows yourself — from an API, a variable, another block — and you own the query, the paging inputs ({{ id.content.page.offset }} and {{ id.content.page.size }}), and the Total Records count. Use Object when your data is a platform object; use Mapped for external APIs and computed data.
When I select a row and open a drawer, why does the drawer show nothing?
Binding the drawer's fields to {{ table1.selectedRow }} fills the content but does not open the drawer. You need two steps: bind the content, and wire the table's On Select Row event to the drawer's Show method via a Trigger Component Method action. Without that method call, the drawer never opens.
Why does my mapped table show the same rows on every page?
Your data source is returning rows in an unstable order, or you are not passing the current offset to it. The table never slices data itself — it asks the source for each page by offset and size via {{ id.content.page.offset }} and {{ id.content.page.size }}. Bind those into your API call's parameters and sort the source deterministically (add a tiebreaker like an id) so page 2 today is the same as page 2 later.
Can I have more than one Table on the same page?
Yes — each Table block has its own Block ID and its own independent state. You can wire them together (bind the second table's data source to the first table's {{ table1.selectedRow.id }} as a filter parameter) to build master-detail layouts.
Why are my columns missing after I switched the data source?
Changing the data source regenerates the column list from the new response and replaces everything you configured — column names, formats, widths, editability are all reset. This is by design. Reconfigure columns after switching sources, not before.
Related Blocks
| Block | Relationship |
| List | A simpler, card-style list without columns, sorting or inline editing |
| Form | Often paired with a table to create or edit the selected record |
| Kanban | An alternative column-based view of the same records |
| Repeatable | Renders one free-form card per record — use when each record needs its own layout |
| Drawer / Modal | Typically opened from a table row action or On Select Row event |