Skip to content

Custom Table Component ​

Inertia Table may be used in a fully headless mode. Build your own Vue or React component and use useTable for search, filters, sorting, columns, and pagination. Use useActions for selection, actions, and exports. The Laravel table definition remains the same.

To control the default Table component without building your own, see Template Refs.

Advanced usage

The composables on this page are stable and fully supported, but the custom components you build with them are your own. We're happy to help with questions about the useTable and useActions API itself, but we cannot debug or maintain custom table implementations.

Creating a Custom Table ​

Import useTable and useActions from @inertiaui/table-vue or @inertiaui/table-react. Both accept the table resource passed from Laravel as the users prop.

In Vue, pass a getter so the composables can read the latest resource after navigation. Their state and computed values are refs. Import reactive from Vue to unwrap them, as below. React hooks return plain values; call them inside your component.

ts
const table = reactive(useTable(() => props.users))
const actions = reactive(useActions(() => props.users))
tsx
const table = useTable(users)
const actions = useActions(users)

Render rows from users.results.data, using each item's _primary_key as its key. Use the resource's column definitions for headers and cells, and table.state.columns to determine which columns are visible. In Vue, import computed to keep the visible columns reactive:

ts
const columns = computed(() => props.users.columns.filter((column) => table.state.columns[column.attribute]))
tsx
const columns = users.columns.filter((column) => table.state.columns[column.attribute])

For sortable headers, call table.sortByColumn(column) and read the current direction with table.isSortedByColumn(column). For pinned columns, use table.leftStickyColumns and table.rightStickyColumns to calculate sticky positions in your layout.

Search and Filters ​

Bind a search input to table.state.search and call setSearch when it changes. The composable handles navigation and the configured debounce time:

vue
<input
    data-testid="example-search"
    type="search"
    :value="table.state.search ?? ''"
    @input="table.setSearch(($event.target as HTMLInputElement).value)"
/>
tsx
<input
    data-testid="example-search"
    type="search"
    value={table.state.search ?? ''}
    onChange={(event) => table.setSearch(event.target.value)}
/>

Call setFilter(attribute, clause, value) to enable and update a filter. Match the value to its clause: ranges take two values, in and not_in take arrays, and clauses such as is_true and is_not_set need no value. A single-choice set filter still uses an array for an in clause. See Filtering for the available filter types and clauses.

To load the options for a lazy filter, call loadFilterOptions with its attribute. The following example receives table as a prop. Import the TableFilter type from @inertiaui/table-vue or @inertiaui/table-react:

ts
function addFilter(filter: TableFilter) {
    props.table.addFilter(filter)

    if (filter.lazy) {
        props.table.loadFilterOptions(filter.attribute)
    }
}
tsx
function addFilter(filter: TableFilter) {
    table.addFilter(filter)

    if (filter.lazy) {
        table.loadFilterOptions(filter.attribute)
    }
}

Read the options from table.loadedFilterOptions[filter.attribute], falling back to filter.options for filters whose options are included in the resource.

Selection and Actions ​

To select a row, call toggleItem with its primary key. Use actions.selectedItems to determine whether its checkbox is checked. Disable the checkbox when _is_selectable is false or an action is running:

vue
<input
    type="checkbox"
    :aria-label="`Select ${user.name}`"
    :disabled="user._is_selectable === false || actions.isPerformingAction"
    :checked="actions.selectedItems.includes(user._primary_key!)"
    @change="actions.toggleItem(user._primary_key!)"
/>
tsx
<input
    type="checkbox"
    aria-label={`Select ${user.name}`}
    disabled={user._is_selectable === false || actions.isPerformingAction}
    checked={actions.selectedItems.includes(user._primary_key!)}
    onChange={() => actions.toggleItem(user._primary_key!)}
/>

toggleItem('*') follows the table's configured bulk-selection mode. Use allItemsAreSelected and someItemsAreSelected for the select-all checkbox.

To run an action, call performAction(action, keys). Your component must check whether the action is authorized and enabled, show a confirmation dialog when confirmationRequired is true, and handle any errors. Use isPerformingAction to indicate that the action is running.

For Row Actions, getActionForItem(action, item._actions[index]) returns the visibility and bindings for that item. For links, navigate to the item's action URL.

Custom actions return an onFinish callback from performAction. Call it after your custom work to clear the busy state and selection. See Row Actions and Bulk Actions for action configuration.

Pagination and Reloading ​

Use setPerPage to change the page size. Pass the server's next_page_url and prev_page_url to visitPaginationUrl; this works with full, simple, and cursor pagination. Here, resource is the table resource and busy combines table.isNavigating and actions.isPerformingAction:

vue
<button type="button" :disabled="busy || !resource.results.next_page_url" @click="table.visitPaginationUrl(resource.results.next_page_url!)">
    Next page
</button>
tsx
<button
    type="button"
    disabled={busy || !resource.results.next_page_url}
    onClick={() => table.visitPaginationUrl(resource.results.next_page_url!)}
>
    Next page
</button>

reloadTable refreshes the table and its configured reload props without clearing the current state.

User Card Grids ​

The same APIs work with lists and card grids. Render users.results.data as cards, use _primary_key as the key, and check table.state.columns[attribute] to decide which fields to display. Search, filters, selection, actions, and pagination remain the same.

You may display 25 cards per page by calling perPageOptions([25, 50, 100]) on the Laravel table. The number of cards in each row is controlled by your CSS; it does not affect pagination.

Exports and Saved Views ​

Download exports use their signed URL, with selected keys added when limitToSelectedRows is enabled. Queued exports use performAsyncExport; display the returned response.dialogTitle and response.dialogMessage when provided. Only enable an export when its authorized property is true. See Exporting for configuration.

Save and delete views through the URLs provided in resource.views, and apply a view with putState(view.state). Keep the existing list of views when handling partial reloads, as these responses contain an empty views list.

useTable uses resource.state as its initial state. Updating the resource prop does not reset this state. To change it, use methods such as setFilter and setSearch, or pass a state object to putState.

The Resource Object ​

The resource prop is the data that is passed to your component, originating from the Laravel backend. The resource object contains the following data:

KeyDescription
actionsThe actions of the table
columnsThe columns of the table
debounceTimeThe debounce time for the search input and filters
defaultPerPageThe default per page value
defaultSortThe default sort value
exportsThe exports of the table
filtersThe filters of the table
nameThe name of the table
paginationWhether the table should paginate
perPageOptionsThe options for the per page select
resultsThe results of the query (the actual data)
searchThe attributes that are searchable
stateThe state of the table

Columns ​

The columns key is an array of objects that define the columns of the table. Each column object has the following properties:

KeyDescription
alignmentThe alignment of the column (left, center, right)
attributeThe attribute of the Eloquent model
headerThe title of the column
metaThe custom Metadata of the column
sortableWhether the column is sortable
toggleableWhether the column can be toggled
visibleByDefaultWhether the column is visible by default

Filters ​

The filters key is an array of objects that define the filters of the table. Each filter object has the following properties:

KeyDescription
attributeThe attribute of the Eloquent model that should be filtered
clausesThe available clauses of the filter
labelThe label of the filter
metaThe custom Metadata of the filter
typeThe type of the filter (text, numeric, set, date, boolean)

Actions ​

The actions key is an array of objects that define the actions of the table. Each action object has the following properties:

KeyDescription
asBulkActionWhether the action should be displayed as a bulk action
asRowActionWhether the action should be displayed as a row action
authorizedWhether the user is authorized to perform the action
confirmationCancelButtonThe label of the confirmation dialog cancel button
confirmationConfirmButtonThe label of the confirmation dialog confirm button
confirmationMessageThe message of the confirmation dialog
confirmationRequiredWhether the action requires confirmation
confirmationTitleThe title of the confirmation dialog
dataAttributesThe custom Data Attributes of the action
iconThe custom icon of the action
isLinkWhether the action is a link
labelThe label of the action
metaThe custom Metadata of the action
showLabelWhether the label should be displayed
variantThe button or link variant
urlThe URL of the action

Exports ​

The exports key is an array of objects that define the exports of the table. Each export object has the following properties:

KeyDescription
labelThe label of the export
authorizedWhether the user is authorized to perform the export
urlThe URL of the export
dataAttributesThe custom Data Attributes of the export
metaThe custom Metadata of the export

State ​

The state key is an object that contains the state of the table. The state object has the following properties:

KeyDescription
columnsAn object that contains the visibility of the columns
filtersAn object that contains the filter state (enabled, value, clause)
perPageThe per page value
searchThe search value
sortThe sort value
stickyThe pinned column attributes
page / cursorThe pagination state

Metadata ​

You may attach metadata to Actions and Links, Columns, Exports, and Filters. The default Table component ignores this metadata, but it's useful when building a custom Table component.