Skip to content

useTable Hook

For full control over the table markup, use useTable directly. It provides all the logic while you control the rendering.

tsx
import { useTable } from 'inertia-table-react';
import { flexRender } from '@tanstack/react-table';

function CustomTable({ tableData }) {
    const {
        table,          // TanStack table instance
        columns,        // BuiltColumn[] for simple iteration
        searchTerm,
        onSearch,
        onPageChange,
    } = useTable({ tableData });

    return (
        <div>
            <input
                value={searchTerm}
                onChange={(e) => onSearch(e.target.value)}
            />
            <table>
                <thead>
                    {table.getHeaderGroups().map((hg) => (
                        <tr key={hg.id}>
                            {hg.headers.map((h) => (
                                <th key={h.id}>
                                    {flexRender(h.column.columnDef.header, h.getContext())}
                                </th>
                            ))}
                        </tr>
                    ))}
                </thead>
                <tbody>
                    {table.getRowModel().rows.map((row) => (
                        <tr key={row.id}>
                            {row.getVisibleCells().map((cell) => (
                                <td key={cell.id}>
                                    {flexRender(cell.column.columnDef.cell, cell.getContext())}
                                </td>
                            ))}
                        </tr>
                    ))}
                </tbody>
            </table>
        </div>
    );
}
vue
<script setup lang="ts">
import { useTable } from 'inertia-table-vue';
import { FlexRender } from '@tanstack/vue-table';
import { useSlots } from 'vue';

const props = defineProps<{ tableData: any }>();
const slots = useSlots();

const {
    table,          // TanStack table instance
    columns,        // BuiltColumn[] for simple iteration
    searchTerm,
    onSearch,
    onPageChange,
} = useTable(props, slots);
</script>

<template>
    <div>
        <input
            :value="searchTerm"
            @input="onSearch(($event.target as HTMLInputElement).value)"
        />
        <table>
            <thead>
                <tr v-for="hg in table.getHeaderGroups()" :key="hg.id">
                    <th v-for="h in hg.headers" :key="h.id">
                        <FlexRender :render="h.column.columnDef.header" :props="h.getContext()" />
                    </th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="row in table.getRowModel().rows" :key="row.id">
                    <td v-for="cell in row.getVisibleCells()" :key="cell.id">
                        <FlexRender :render="cell.column.columnDef.cell" :props="cell.getContext()" />
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</template>

Return Value

PropertyTypeDescription
tableTanStack Table<Row>Full TanStack table instance
columnsBuiltColumn[]Simple array with renderHeader() and renderCell()
classNamesClassNamesMerged default + custom class names
searchTermstringCurrent search value
onSearch(term) => voidDebounced search handler
hasExternalSearchbooleanWhether an external searchRef is in use
sortBystring | nullActive sort column
sortDir'asc' | 'desc'Sort direction
onSort(sortKey) => voidSort toggle handler
getSortState(sortKey) => SortStateGet sort state for a column
onPageChange(page) => voidNavigate to page
isProcessingbooleantrue while an Inertia navigation is in progress

All URL parameters are automatically scoped when the table has an identifier set in PHP. No additional frontend configuration is needed.

The isProcessing flag is set automatically when Inertia starts a navigation and cleared when new data arrives. Use it to show loading states in custom table implementations (the default InertiaTable component handles this automatically).

Simple Iteration

If you don't need TanStack features, use columns directly:

tsx
const { columns } = useTable({ tableData });

<table>
    <thead>
        <tr>
            {columns.map((col) => <th key={col.id}>{col.renderHeader()}</th>)}
        </tr>
    </thead>
    <tbody>
        {tableData.data.map((row, i) => (
            <tr key={row.id}>
                {columns.map((col) => <td key={col.id}>{col.renderCell(row, i)}</td>)}
            </tr>
        ))}
    </tbody>
</table>
vue
<script setup lang="ts">
import { useTable } from 'inertia-table-vue';
import { useSlots } from 'vue';

const props = defineProps<{ tableData: any }>();
const slots = useSlots();

const { columns } = useTable(props, slots);
</script>

<template>
    <table>
        <thead>
            <tr>
                <th v-for="col in columns" :key="col.id">{{ col.renderHeader() }}</th>
            </tr>
        </thead>
        <tbody>
            <tr v-for="(row, i) in tableData.data" :key="row.id">
                <td v-for="col in columns" :key="col.id">
                    <component :is="col.renderCell(row, i)" />
                </td>
            </tr>
        </tbody>
    </table>
</template>