Skip to content

Template Refs ​

Template refs give you access to the same useTable and useActions methods you'd get from a custom table, without actually building one. Attach a ref to the default <Table> component, then call its exposed methods from anywhere in your page (add or remove filters, toggle row selection, change sort, trigger actions) to drive the table from custom buttons, keyboard shortcuts, or other components.

Basic Usage ​

To access the table's methods, create a template ref and call the exposed methods:

vue
<script setup>
import { ref } from 'vue'
import { Table } from '@inertiaui/table-vue'

const props = defineProps({
    users: Object,
})

const tableRef = ref()

function addFilter() {
    const filter = props.users.filters.find(f => f.attribute === 'status')
    tableRef.value.addFilter(filter)
}

function selectAll() {
    tableRef.value.toggleItem('*')
}
</script>

<template>
    <Table ref="tableRef" :resource="users" />
</template>
jsx
import { useRef } from 'react'
import { Table } from '@inertiaui/table-react'

export default function Users({ users }) {
    const tableRef = useRef()

    function addFilter() {
        const filter = users.filters.find(f => f.attribute === 'status')
        tableRef.current.addFilter(filter)
    }

    function selectAll() {
        tableRef.current.toggleItem('*')
    }

    return <Table ref={tableRef} resource={users} />
}

Table Methods ​

MethodDescription
addFilter(filter)Add a filter
removeFilter(filter)Remove a filter
resetAllFilters()Remove all active filters
setFilter(attribute, clause, value)Set a filter by attribute, clause, and value
setSearch(search)Set the search query
toggleColumn(column)Show/hide a column
setSort(sort)Set the table sort
sortByColumn(column)Cycle a column through ascending, descending, and default sort
loadFilterOptions(attribute)Load options for a lazy filter
setPerPage(perPage)Change items per page
makeSticky(column)Make columns sticky
undoSticky(column)Remove sticky positioning
putState(newState)Replace the table state
reloadTable()Reload the table and its configured partial reload props
toggleItem(key)Toggle item selection (use '*' for all)
performAction(action, keys)Execute an action after displaying any required confirmation
performAsyncExport(export)Start a queued export
visitPaginationUrl(url)Navigate using a server-provided pagination URL
visitTableUrl(url, options)Visit a table URL with Inertia options

Table State ​

PropertyTypeDescription
stateObjectReactive table state
hasFiltersBooleanWhether filters are active
hasSelectableRowsBooleanWhether rows are selectable
selectedItemsArraySelected item keys
allItemsAreSelectedBooleanWhether all items in the configured selection scope are selected
someItemsAreSelectedBooleanWhether the selection is partial
isPerformingActionBooleanWhether an action or export is running
loadedFilterOptionsObjectLoaded options keyed by filter attribute
isNavigatingBooleanWhether navigating

Accessing State ​

You may access the table's reactive state. In Vue, the state is automatically reactive. In React, access the state in response to user interactions or component lifecycle events:

vue
<script setup>
import { ref } from 'vue'
import { Table } from '@inertiaui/table-vue'

const props = defineProps({
    users: Object,
})

const tableRef = ref()

function checkState() {
    // Access state when needed
    const selectedItems = tableRef.value.selectedItems
    const currentSearch = tableRef.value.state.search

    console.log(`Selected ${selectedItems.length} items`)
    console.log(`Current search: ${currentSearch}`)
}
</script>

<template>
    <div>
        <button @click="checkState">
            Check State
        </button>
        <Table ref="tableRef" :resource="users" />
    </div>
</template>
jsx
function MyComponent({ users }) {
    const tableRef = useRef()

    function checkState() {
        // Access state when needed
        const selectedItems = tableRef.current.selectedItems
        const currentSearch = tableRef.current.state.search

        console.log(`Selected ${selectedItems.length} items`)
        console.log(`Current search: ${currentSearch}`)
    }

    return (
        <div>
            <button onClick={checkState}>
                Check State
            </button>
            <Table ref={tableRef} resource={users} />
        </div>
    )
}

Observing Changes ​

The Table emits change in Vue and calls onChange in React with { table, actions }. Use these to display the current search, selection, or other state outside the component.

In Vue, bind @change="handleChange" to the Table and render status where you need it. Import ref from Vue and the UseTableReturn and UseActionsReturn types from @inertiaui/table-vue:

ts
const status = ref('No selection')

function handleChange({ table, actions }: { table: UseTableReturn; actions: UseActionsReturn }) {
    status.value = `${actions.selectedItems.value.length} selected; search: ${table.state.value.search || 'none'}`
}

Vue unwraps the values exposed through the template ref, so you may use tableRef.value.state.search in JavaScript. The change event and Slots receive refs instead. Use .value to access them, for example table.state.value.search and actions.selectedItems.value.

In React, changing a ref does not trigger a render. Use onChange to copy the values your controls need into React state. The callback receives new table and actions objects on each render. Compare the values before updating state to avoid a render loop.

Observing multiple values in React

The following example stores the table and action state for use in external controls. Import useState and useCallback from React, and TableResource, UseTableReturn, and UseActionsReturn from @inertiaui/table-react:

tsx
interface TableScope {
    resource: TableResource
    table: UseTableReturn
    actions: UseActionsReturn
}

Inside your component, compare the values used by the controls before updating state:

tsx
const [scope, setScope] = useState<TableScope | null>(null)

const handleChange = useCallback(
    ({ table, actions }: { table: UseTableReturn; actions: UseActionsReturn }) => {
        setScope((current) => {
            if (
                current?.resource === users &&
                current.table.state === table.state &&
                current.table.loadedFilterOptions === table.loadedFilterOptions &&
                current.table.isNavigating === table.isNavigating &&
                current.actions.selectedItems === actions.selectedItems &&
                current.actions.isPerformingAction === actions.isPerformingAction
            ) {
                return current
            }
            return { resource: users, table, actions }
        })
    },
    [users],
)

Pass handleChange to the Table's onChange prop. Once scope is available, use scope.table and scope.actions to render your controls. To reload the table, call tableRef.current.reloadTable().

Each useTable instance has its own state. To control an existing Table, use its ref instead of calling useTable(users) again. Updating the resource prop does not reset the Table's state; use methods such as setFilter, setSearch, or putState to update it.

Custom Filter Controls ​

You may build custom filter interfaces that integrate with the table's filtering system. setFilter enables a filter and sets its clause and value in one call, without needing to call addFilter first:

vue
<script setup>
import { ref } from 'vue'
import { Table } from '@inertiaui/table-vue'

const props = defineProps({
    users: Object,
})

const tableRef = ref()

function applyStatusFilter(status) {
    tableRef.value.setFilter('status', 'equals', status)
}

function clearFilters() {
    tableRef.value.resetAllFilters()
}
</script>

<template>
    <div class="mb-4 space-x-2">
        <button @click="applyStatusFilter('active')">Show Active</button>
        <button @click="applyStatusFilter('inactive')">Show Inactive</button>
        <button @click="clearFilters">Clear Filters</button>
    </div>

    <Table ref="tableRef" :resource="users" />
</template>
jsx
function MyComponent({ users }) {
    const tableRef = useRef()

    function applyStatusFilter(status) {
        tableRef.current.setFilter('status', 'equals', status)
    }

    function clearFilters() {
        tableRef.current.resetAllFilters()
    }

    return (
        <div>
            <div className="mb-4 space-x-2">
                <button onClick={() => applyStatusFilter('active')}>
                    Show Active
                </button>
                <button onClick={() => applyStatusFilter('inactive')}>
                    Show Inactive
                </button>
                <button onClick={clearFilters}>
                    Clear Filters
                </button>
            </div>

            <Table ref={tableRef} resource={users} />
        </div>
    )
}

Use setSearch to update the search query programmatically:

vue
<script setup>
import { ref } from 'vue'
import { Table } from '@inertiaui/table-vue'

const props = defineProps({
    users: Object,
})

const tableRef = ref()

function searchForUser(name) {
    tableRef.value.setSearch(name)
}
</script>

<template>
    <div class="mb-4 space-x-2">
        <button @click="searchForUser('John')">Search for John</button>
        <button @click="searchForUser('')">Clear Search</button>
    </div>

    <Table ref="tableRef" :resource="users" />
</template>
jsx
function MyComponent({ users }) {
    const tableRef = useRef()

    function searchForUser(name) {
        tableRef.current.setSearch(name)
    }

    return (
        <div>
            <div className="mb-4 space-x-2">
                <button onClick={() => searchForUser('John')}>
                    Search for John
                </button>
                <button onClick={() => searchForUser('')}>
                    Clear Search
                </button>
            </div>

            <Table ref={tableRef} resource={users} />
        </div>
    )
}

Custom Bulk Actions ​

You may trigger bulk actions programmatically and control row selection:

vue
<script setup>
import { ref } from 'vue'
import { Table } from '@inertiaui/table-vue'

const props = defineProps({
    users: Object,
})

const tableRef = ref()

function selectAll() {
    tableRef.value.toggleItem('*')
}

function bulkDelete() {
    const deleteAction = props.users.actions.find(a => a.id === 'delete')

    if (deleteAction && tableRef.value.selectedItems.length > 0) {
        tableRef.value.performAction(deleteAction, tableRef.value.selectedItems)
    }
}
</script>

<template>
    <div class="mb-4 space-x-2">
        <button @click="selectAll">Toggle All</button>
        <button @click="bulkDelete" :disabled="!tableRef?.selectedItems?.length">
            Delete Selected ({{ tableRef?.selectedItems?.length || 0 }})
        </button>
    </div>

    <Table ref="tableRef" :resource="users" />
</template>
jsx
function MyComponent({ users }) {
    const tableRef = useRef()

    function selectAll() {
        tableRef.current.toggleItem('*')
    }

    function bulkDelete() {
        const deleteAction = users.actions.find(a => a.id === 'delete')

        if (deleteAction && tableRef.current.selectedItems.length > 0) {
            tableRef.current.performAction(deleteAction, tableRef.current.selectedItems)
        }
    }

    return (
        <div>
            <div className="mb-4 space-x-2">
                <button onClick={selectAll}>Toggle All</button>
                <button onClick={bulkDelete}>
                    Delete Selected
                </button>
            </div>

            <Table ref={tableRef} resource={users} />
        </div>
    )
}