Custom table layouts
Pass children to <AppTable> to render a different layout. Use
regions when you only need to replace part of the table.
Render a name list
Section titled “Render a name list”This example expects a name column. It uses one snapshot for both the result and request status:
React
import { AppTable } from '@/components/app-table';import { useTableController, useTableSnapshot } from '@inertiax/react-table';
function NameList() { const snapshot = useTableSnapshot(); const controller = useTableController(); const busy = snapshot.status.type === 'pending' || snapshot.status.type === 'scheduled';
if (snapshot.outcome === 'problem') { return ( <div role="alert" aria-busy={busy}> <p>This table link needs updating.</p> {snapshot.status.type === 'error' ? ( <button type="button" onClick={() => controller.retry()}>Retry</button> ) : snapshot.canReset ? ( <button type="button" disabled={busy} onClick={() => controller.resetProblem()}> Reset table settings </button> ) : <p>Open a new table link to continue.</p>} </div> ); }
const { component } = snapshot.envelope; return ( <div aria-busy={busy}> {busy && <p role="status">Loading names…</p>} {(snapshot.status.type === 'error' || snapshot.status.type === 'externalError') && ( <p role="alert"> Could not update the list.{' '} {snapshot.status.type === 'error' && ( <button type="button" onClick={() => controller.retry()}>Retry</button> )} </p> )} {component.data.rows.length === 0 ? <p>No matching names.</p> : ( <ul> {component.data.rows.map((row) => ( <li key={String(row[component.data.rowKey]?.value)}> {String(row.name?.value ?? '')} </li> ))} </ul> )} </div> );}
<AppTable prop="users"><NameList /></AppTable>;Replacement children own the whole UI. This list displays the current page; add the table controls for search, filters, sorting, and pagination, or build your own with the controller, for example for selection. Previous rows remain visible during a reload or a failed request. An invalid-link problem has no successful result, so that branch provides reset or retry instead.
useTableSnapshot() works for both outcomes. Hooks that require a result, such as
useTableEnvelope() and useTableState(), must only mount after a successful outcome.
Add search, filters, sorting, and pagination
Section titled “Add search, filters, sorting, and pagination”Place TableSearch, TableFilters, TableSort, and TablePagination around your own layout to
add search, filters, sorting, and pagination. This example shows orders as cards and expects
reference and customer_name columns:
React
import { AppTable } from '@/components/app-table';import { TableFilters, TablePagination, TableSearch, TableSort, useTableSnapshot,} from '@inertiax/react-table';
function OrderCards() { const snapshot = useTableSnapshot(); if (snapshot.outcome === 'problem') return <p role="alert">This table link needs updating.</p>;
const { data } = snapshot.envelope.component; return ( <ul className="grid gap-4 sm:grid-cols-2"> {data.rows.map((row) => ( <li key={String(row[data.rowKey]?.value)}> <strong>{String(row.reference?.value ?? '')}</strong> <p>{String(row.customer_name?.value ?? '')}</p> </li> ))} </ul> );}
<AppTable prop="orders"> <div className="flex flex-wrap gap-2"> <TableSearch /> <TableSort /> <TableFilters /> </div> <OrderCards /> <TablePagination /></AppTable>;Each control renders nothing when the table doesn’t offer its feature, so the same controls work
with any table. They also render nothing for a problem outcome, so they can stay outside your
layout’s success branch. They use the table’s texts and theme, and TableSort sets one sort key
at a time. This example leaves out the loading, empty, error, and reset handling a custom layout
owns; add it as in the name list. See
Layout controls for each component.
Customize the problem region
Section titled “Customize the problem region”Use customization.texts.problem to change the wording. For a custom layout, use problemState:
React
<AppTable prop="orders" customization={{ regions: { problemState: ({ canReset, controller, status }) => ( <div role="alert"> <p>This table link needs updating.</p> {status.type === 'error' ? ( <button type="button" onClick={() => controller.retry()}>Retry</button> ) : canReset ? ( <button type="button" disabled={status.type === 'pending' || status.type === 'scheduled'} onClick={() => controller.resetProblem()} > Reset table settings </button> ) : <p>Open a new table link to continue.</p>} </div> ), }, }}/>The region receives componentId, problem, controller, status, and canReset.
Unlike the loading, empty and request-error regions, it replaces the whole table and returns
ordinary markup, not table rows. See Regions for the full contract.