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
| Property | Type | Description |
|---|---|---|
table | TanStack Table<Row> | Full TanStack table instance |
columns | BuiltColumn[] | Simple array with renderHeader() and renderCell() |
classNames | ClassNames | Merged default + custom class names |
searchTerm | string | Current search value |
onSearch | (term) => void | Debounced search handler |
hasExternalSearch | boolean | Whether an external searchRef is in use |
sortBy | string | null | Active sort column |
sortDir | 'asc' | 'desc' | Sort direction |
onSort | (sortKey) => void | Sort toggle handler |
getSortState | (sortKey) => SortState | Get sort state for a column |
onPageChange | (page) => void | Navigate to page |
isProcessing | boolean | true 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>