tooinfinity/inertia-datatables
Inertia DataTables for Laravel: server-side tables, filters, and sorting with an Inertia-friendly API. Zero stars, but a small, focused package for building fast data tables without leaving your Inertia stack.
This is a v0.0.1 pre-release, so treat the first hour as reconnaissance rather than integration. Skim the source before you write any app code — on a 0.0.1 the README will be thinner than the implementation, and the column/filter definition objects are where the real API lives.
Environment floor (hard):
If your app is on Laravel 11, PHP 8.3, React 18, or Inertia 2, stop now — this package cannot be retrofitted into that stack without upgrading the whole frontend. Verify which Inertia major you're actually running before composer require; the release notes pin to Inertia 3.
Install order:
composer require the package, then publish the config (vendor:publish — confirm the tag from the service provider) so you can see the query parameter names before writing any frontend code.useDataTable hook; check the hook's signature against the props your controller returns before wiring UI.Where to look first: the config file (parameter names, defaults), then the column allow-list implementation, then useDataTable. Those three define the entire contract between backend and frontend.
First use case: a single simple index — e.g. a Users list with server-side pagination, global search over name/email, and one boolean filter (active). That exercises the full loop (query building → allow-list → Inertia prop → hook state → debounced partial reload) with no relation joins to muddy the debugging. Get that page working end-to-end before touching select filters or column search.
Server owns the schema, client mirrors it. Define columns and filters in the controller/action, return the datatable payload as an Inertia prop, and keep the React column definitions purely presentational. The allow-list is the contract; the TS types are the mirror. When they drift you get silent no-ops (see Gotchas), so keep the two definitions physically adjacent in the repo.
Eloquent vs Query Builder:
with() relations on the returned rows.user.company.name you'll need an explicit join and a declared column alias, or a subquery select.Partial reloads are the point. Drive every table interaction through Inertia visits with only: ['users'] (or whatever your prop is named) so search/sort/page changes don't re-render the layout or re-run unrelated page queries. This is what makes the debounced search feel cheap. If you find yourself doing router.reload() without only, you're leaving the package's main performance benefit on the table.
Filters: text/select/boolean cover most admin UIs. Validate the filter payload in a Form Request before it reaches the datatable — select filters especially, since an unvalidated value that happens to match a column name can broaden what the builder accepts. Treat the filter value as data, never as a column reference.
Frontend composition: useDataTable holds page/sort/search/filter state and debounces the search. Feed Toolbar the filter controls, ColumnHeader the sort affordances, and render ColumnVisibility/Empty/Pagination off the same state. Don't lift table state into a parent store — the hook assumes it owns the visit lifecycle.
Testing: Testbench makes this cheap. Write regression tests for the security boundary specifically: a request sorting on a non-allow-listed column must be rejected or ignored, and a filter with an unknown value must not alter the query. Those tests are what let you upgrade a 0.0.x package with any confidence.
v0.0.1 the prop shape, column definition object, and even the query parameter names can change between patches. Pin the exact version ("x.y.z", not ^), and treat every bump as a manual diff-and-review.sort/column/filter key into orderBy or whereColumn outside the package's resolution path. Confirm what the package does with a disallowed column — silently ignore vs. throw — and assert that behavior in a test. Silent ignoring hides typos; throwing is easier to debug. Know which you have.count(*) is still a full scan. Server-side pagination runs a count query each request. With joins or large tables, add a covering index on your default sort column and watch for slow counts before you blame the package. Consider a keyset/simplePaginate path for very large datasets if the package exposes one.npm install resolves but the table silently fails to hydrate, check for a duplicated @tanstack/react-table or a second React copy in node_modules.->toSql() with bindings) immediately before execution. The generated SQL instantly tells you whether your sort column survived the allow-list, whether the filter applied, and whether a join caused duplicate rows.searchable/sortable flags, the base filter class (for a custom date-range or enum filter), and whether column definitions are a fluent builder or a plain array. On 0.0.1, extending an undocumented base class is the riskiest thing you can do — isolate any custom filter behind a thin wrapper so a breaking change is a one-file fix.How can I help you explore Laravel packages today?