Download the PHP package toolbelt/inertia-table without Composer

On this page you can find all versions of the php package toolbelt/inertia-table. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package inertia-table

Musing Inertia Table

PHP tests JavaScript tests Latest Version on Packagist Total Downloads

Server-driven data tables for Laravel and Inertia.js. Define the table once in PHP—columns, sorting, search, filters and actions—and render it in Vue with one component.

Toolbelt keeps the server authoritative. The browser can only request capabilities declared by the table, URL state is namespaced per table, and query execution is powered by Spatie Laravel Query Builder.

[!WARNING] The package is actively developed before v1.0. Please expect API changes between minor releases.

Highlights

Requirements

Layer Requirement
PHP 8.3+
Laravel 12 or 13
Inertia Laravel 2 or 3; Vue 3.4+
Query engine Spatie Laravel Query Builder 7
Frontend peers Tailwind CSS 4.1+, Reka UI 2.10+, @lucide/vue 1.30+

Installation

Install the Laravel core and Vue renderer:

Releases

The Laravel package is distributed through Packagist and the Vue renderer through npm. A pushed Git tag in the form vX.Y.Z is the release trigger. The tag must match the version in package.json.

Before the first release, submit https://github.com/thienbd203/inertia-table to Packagist and add an NPM_TOKEN repository secret with permission to publish @musing/inertia-table-vue. The release workflow runs PHP and JavaScript checks, then publishes the Vue package with npm provenance.

Publish configuration only when you want to change pagination or debounce defaults:

Saved views are opt-in. Publish and run their migration before enabling them on a table:

Tailwind CSS v4

The renderer contains Vue source that Tailwind must scan. Add this to the host application's stylesheet:

The package uses your existing shadcn/Tailwind CSS variables. It does not require an application @/components/ui alias.

Quick start

Generate a dedicated table class when the table needs row actions, bulk actions, Saved Views or exports:

The command writes to app/Tables, infers a singular model when --model is omitted, and refuses to replace an existing class unless --force is passed.

Create a table definition. This is the source of truth for what users can do.

Pass it directly to the Inertia page:

Then render it. For most screens this is all the frontend code required.

Anonymous tables

Simple read-only tables can be defined inline without a dedicated class. The resource may be an Eloquent model class or an existing Eloquent builder; the builder is cloned before each resolution so the table cannot mutate the controller's query.

Table::build() also accepts search, pagination, debounceTime, withQueryBuilder, emptyState and stickyHeader. Set pagination: false to return the complete normalized result and remove page controls. Anonymous tables intentionally do not declare actions, exports or Saved Views; use a generated class when server-managed behavior or persisted state is required.

Internationalization

The Vue renderer defaults to English and ships with Vietnamese messages. Configure it once on the Vue app:

The configuration uses Vue provide/inject, so each SSR application instance keeps its own locale. A table can override the app defaults without changing other tables:

locale controls calendars and locale-sensitive UI. messages controls package-owned interface text. Application-owned labels such as column names, filter options and action labels should still be translated by the host application.

Laravel-owned defaults follow app()->getLocale() automatically. Publish the language files when an application needs to customize them:

Every Vietnamese frontend key is type-checked against the English catalog. Unknown interpolation placeholders are left visible rather than silently removed. See docs/customization.md for the ownership rules, icon overrides, slots, stable CSS hooks and headless customization API.

Columns

Built-in types: TextColumn, NumberColumn, NumericColumn, BadgeColumn, BooleanColumn, DateColumn, DateTimeColumn, ImageColumn and ActionColumn.

All content columns support common presentation methods such as sortable(), searchable(), toggleable(), visible(), headerClass(), cellClass(), tooltip(), alignment, wrapping and truncation.

ActionColumn::asDropdown() groups each row's actions behind one accessible menu trigger. Dynamic action(<key>) slots work in both the inline and dropdown renderers.

Sticky header and columns

Enable a sticky header on one table with a property or the fluent API. The default renderer gives sticky-header tables a 70vh scroll viewport so the header has a vertical scroll container; override --tb-sticky-header-max-height on the wrapper when a screen needs another height.

stickable() lets the user pin or unpin a column from its header menu. sticky() makes the column permanently pinned and works with every column type, including ActionColumn:

The pin side is inferred from the column's visible position. Adjacent pinned columns stack measured widths, hidden columns retain their pin preference, and offsets are recalculated after visibility or responsive width changes. Logical CSS insets mirror the leading/trailing groups in RTL layouts. Pin state is namespaced in the table URL and included in Saved Views; it never changes the search/filter identity used by bulk selection.

Use a custom sort for expressions or application-specific ordering:

Badges and images

Navigation

Cell and row URLs can be a string or a Url object. The object carries Inertia navigation options to the renderer.

Tables deliberately do not make rows clickable by default. Handle the optional row-click event when a screen needs it:

Empty states and row data attributes

Return an EmptyState when a genuinely empty base table should offer more than the generic no-results message. It supports a title, message, optional icon, metadata, normalized data-* attributes and URL actions:

The server only serializes this definition when the unfiltered base query is empty. A search or filter that happens to match no rows keeps the ordinary No results found UI. The emptyState slot remains available to replace the default renderer.

Add safe per-row DOM hooks without leaking arbitrary HTML attributes by returning keys without the data- prefix. The callback receives both the model and the transformed row data:

Only scalar or null values are accepted. Package-owned data-selected and data-row-clickable state cannot be overwritten.

Search and filters

Mark columns as searchable() for global search. Override the resolved list on the table when necessary:

Available filter classes and their default behaviour:

Filter Typical clauses
TextFilter contains, starts with, equals, not equals
NumericFilter comparison, equals and range clauses
SetFilter in, not in, equals, not equals
BooleanFilter true / false
DateFilter before, after, equals, and date ranges

SetFilter presents a multi-select UI for in and not_in. DateFilter presents an inline calendar for a single date and a two-month range calendar for between and not_between.

For application-specific query logic, use applyUsing() and retain the declared option allowlist:

SelectFilter is available as a deprecated alias for SetFilter.

Relationship queries

Declared columns and filters accept nested Eloquent paths. Dot notation is never read directly from client input: only paths present in columns() or filters() can become query constraints.

Search and filters use nested whereHas constraints, so has-many matches do not duplicate base rows. Direct columns are qualified with the model table to avoid ambiguous-column errors. Nullable relationship filters include a missing relation when using is_not_set.

To-one relationship sorting uses the optional Eloquent Power Joins adapter and a left join, preserving rows whose relationship is null:

To-many sorting stays duplicate-safe by ordering on a correlated MIN for ascending order or MAX for descending order. Use sortUsing() when a domain needs different aggregation or ordering semantics. The global adapter is configured at inertia-table.relationship_sorter.

Tables can customize the same Spatie query builder used by results, explicit and all-matching selections, and every export scope:

When the base query or hook adds joins, the package selects the base model and deduplicates by its qualified primary key so pagination totals and exported rows remain stable. Application-owned joined projections and custom sort callbacks remain responsible for their own SQL portability.

Actions

Actions are server-declared and can be row-level, bulk or both. Authorization, visibility, disabled state and action labels may vary per model.

handle() invokes the closure once per selected model in chunks, which is useful when model events must run. Use chunkSize() to override the default chunk size of 1,000. During bulk execution, unselectable models and models whose row action is unauthorized, disabled or hidden are skipped. handleSelection() invokes the closure once with a typed Selection, which is useful for a set-based update over a large filtered result; because it operates directly on the query, its callback owns any additional per-model eligibility constraints that cannot be represented by selectableQuery().

before() and after() wrap the handler once per action request and receive the Selection. An after() callback may return a response or URL, and after('/topics/archived') provides a direct redirect. Use authorize() for request-level authorization and authorized() for model-level row authorization. Handler actions automatically receive a signed internal POST endpoint under the configured action_path; action scope and availability are checked again when that endpoint runs.

Managed endpoints use Laravel's normal response contract consistently: returned Response/Responsable values pass through, successful handlers without one redirect back, unavailable actions return 403, disabled row actions return validation errors, and unexpected exceptions remain visible to Laravel's exception handler.

The Selection query always starts from Table::query() and applies selectableQuery(). For an all-results selection, it rebuilds the query through the table's declared search/filter allowlist and applies the unchecked keys from except. Useful APIs are query(), count(), get(), firstOrFail() and memory-safe each().

Declare bulk eligibility at both query and row level. The query scope gives the frontend an exact selectableTotal without loading every model; the row check disables individual checkboxes. Keep both rules equivalent whenever possible:

An unselectable row may still expose row actions. Selectability only defines the bulk-selection boundary.

Use endpoint() when an existing application route should own the action instead:

Omit both handle() and endpoint() for a frontend-owned action. The component emits custom-action with (action, keys, onFinish, selection); call onFinish() after the custom work completes. Existing handlers can keep using the first three arguments.

The header checkbox immediately selects every selectable result matching the current search and filters, across all pages. Its label and selected count use the exact server-provided selectableTotal; there is no intermediate "current page" selection step. The checkbox is empty, indeterminate, or checked, and clicking the indeterminate state always resolves to select-all. Individual rows can then be unchecked and are tracked in selection.except. Shift-clicking a row checkbox applies the target checkbox state to the contiguous range from the previously clicked row on the current page while skipping disabled checkboxes.

Explicit bulk selections keep the existing { ids: [...] } request payload. Selecting all matching results sends a selection descriptor instead of attempting to load every ID into the browser:

Managed handlers resolve this descriptor through Selection automatically. Application-owned endpoint() routes remain responsible for resolving it safely and must not apply raw client attributes directly to SQL.

Confirmation text supports :count for row and bulk actions, plus scalar row attributes such as :name. This keeps destructive confirmation copy honest without loading all matching IDs into the browser:

The title and message may instead contain singular, plural, and optional all-matching variants. An all-matching selection uses the third variant and falls back to the plural variant when it is omitted:

Action icons are intentionally library-agnostic. Register your Lucide resolver once:

Exports

Declare one or more authorized export options on the table. Native CSV has no additional dependency and defaults to the full base query:

allRows() uses Table::query() without applying the browser state. filtered() runs the current search, filters and sort through the same server normalization as the visible table. selected() reuses the typed Selection, including all-matching selections and exclusions. A selected export enables row checkboxes even when the table has no bulk actions, and starting a download never clears the current selection.

Columns are exportable by default except ActionColumn. Customize the resolved value or exclude a column without changing its onscreen renderer:

Declared exportable columns are used by default. Call visibleColumnsOnly() on an export when it should follow the normalized column visibility state. Native CSV streams the Eloquent cursor, emits UTF-8 with a BOM by default, and protects spreadsheet formula prefixes. Use meta(['delimiter' => ';', 'bom' => false]) to customize CSV output.

XLSX and PDF use the optional Laravel Excel adapter:

The base package does not require Laravel Excel. Requesting one of those formats without it returns a clear validation error. Custom formats implement Musing\InertiaTable\Contracts\Exporter and are registered under inertia-table.exporters.<type>.

Call queue() when the export should run outside the request. The worker rebuilds the table and query from a normalized, serializable snapshot; it never receives a live request, query builder, table instance or definition closure:

Queue connection, name, delay, disk, path and expiry fall back to inertia-table.queue. Every dispatch carries an idempotency key, so duplicate submissions for the same actor and scoped export reuse the existing job. Completed files are deleted after expiry, and partial files are removed when generation fails. chain() accepts follow-up job objects.

The default context captures the authenticated actor and restores it in the worker before authorization is checked again. Multi-tenant applications can add scalar tenant identifiers with scopeAttributes() and provide an ExportContext implementation via context() to restore and release tenant state. Definitions removed or materially changed after dispatch fail safely instead of exporting with different semantics.

The Vue renderer submits signed POST requests and reads Laravel's CSRF token from either <meta name="csrf-token"> or the XSRF-TOKEN cookie. It exposes export-success, export-queued and export-error events; custom renderers can use the same controller directly:

Queued dispatches expose queuedExport with dispatched, processing, ready, failed or expired state. The package does not start a hidden polling loop. Applications can deliver status through their existing notifications or realtime channel and call updateQueuedExport(status); a ready status with a URL renders a download action. An explicit redirectAfterDispatch() is followed immediately.

Slots and headless API

The default renderer is intended to cover normal tables. Use slots only for targeted customisation.

Useful slots include topbar, beforeSearch, afterSearch, beforeActions, afterActions, filters, table, thead, tbody, footer, loading, emptyState, confirmation, cell(attribute), header(attribute), filter(attribute), image(attribute) and image-fallback(attribute).

Use filter(attribute) when an option source needs application-owned behavior such as remote search, pagination or creating a missing option. The slot receives filter, state, value, update, setDisplayValue, close, table and actions:

Declare the stored value with a regular server-side filter. For example, an integer foreign key can use a clause-less numeric filter:

The package only owns the selected filter value and URL state in this case. The application owns the endpoint, loading state, debounce, result pagination and option creation.

For a fully custom renderer, use the composables instead:

Laravel resources include the model's Eloquent primary key as stable row metadata, so selection also works for UUIDs and primary keys not named id. When rendering an application-owned resource, override the identity explicitly:

The equivalent headless option is useActions(table, { rowKey: (topic) => topic.uuid }). Selection persists across pagination and is cleared when the active search or filters change.

URL state and multiple tables

Every table gets an isolated query-string namespace. Several table resources may live on one Inertia page without overwriting one another.

Toolbelt translates this state to Spatie's query contract internally. Invalid columns, sorts, clauses, filter values and page sizes are ignored or replaced by safe defaults before the query executes.

Saved views

Enable saved views by returning a Views definition from the table. The default scope belongs to the authenticated Laravel user:

The toolbar then provides the view switcher and create, update, rename, delete, default and share operations allowed by the server. View state contains sort, filters, column visibility, pinned-column metadata and page size. Search remains ephemeral unless includeSearch() is enabled:

attributes() isolates otherwise identical tables by application context, such as tenant or workspace. scopeTableName() additionally isolates multiple named instances of the same PHP table class. Use scopeUser(false) for application-wide views, userResolver() for a non-standard identity source, and modelClass() for a TableView subclass. Fine-grained policies are available through authorizeCreate(), authorizeUpdate(), authorizeDelete(), authorizeShare() and authorizeDefault().

State precedence is explicit URL values over the selected view, then the user's default view, then table defaults. Stored values are normalized against the table's current columns, filters and per-page allowlist whenever they are read, so stale definitions cannot restore undeclared query capabilities. CRUD uses signed, CSRF-protected routes and a lock_version; concurrent stale edits are rejected instead of silently overwriting a newer view.

For a custom renderer, compose the view controller with the same table instance:

Development

The design and resource contract are described in docs/architecture.md. Public compatibility guarantees are documented in docs/api-stability.md.

License

The MIT License. See LICENSE.md.


All versions of inertia-table with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
illuminate/contracts Version ^12.0||^13.0
inertiajs/inertia-laravel Version ^2.0||^3.0
spatie/laravel-package-tools Version ^1.16
spatie/laravel-query-builder Version ^7.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package toolbelt/inertia-table contains the following files

Loading the files please wait ...