PHPackages                             inertify/table - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Admin Panels](/categories/admin)
4. /
5. inertify/table

ActiveLibrary[Admin Panels](/categories/admin)

inertify/table
==============

Headless tables for Laravel + Vue + Inertia with pagination, sorting, and filtering.

v1.2.1(1mo ago)01↓100%MITVuePHP ^8.2CI failing

Since Mar 20Pushed 1mo agoCompare

[ Source](https://github.com/enkot/inertify-table)[ Packagist](https://packagist.org/packages/inertify/table)[ RSS](/packages/inertify-table/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (7)Versions (8)Used By (0)

inertify/table
==============

[](#inertifytable)

Headless table tooling for Laravel + Inertia + Vue with:

- Pagination
- Sorting
- Filtering
- Multi-table support on one page (query keys are table-scoped)
- No UI opinions (you render your own markup)

Install
-------

[](#install)

```
composer require inertify/table
```

If you publish config:

```
php artisan vendor:publish --tag=inertify/table-config
```

Optional Vue package build output:

```
npm install @inertify/table-vue
```

Publish to Packagist
--------------------

[](#publish-to-packagist)

1. Make sure your package name in `composer.json` is final (already set to `inertify/table`).
2. Commit and push `main` to a **public** Git repository.
3. Create a semantic version tag and push it:

```
git tag v1.0.0
git push origin v1.0.0
```

4. Sign in to [Packagist](https://packagist.org), click **Submit**, and paste your repository URL.
5. In repository settings (GitHub/GitLab), add Packagist webhook so updates happen automatically.
6. After indexing, install with Composer:

```
composer require inertify/table
```

### Recommended release flow

[](#recommended-release-flow)

- Merge changes to `main`
- Run tests (`composer test`)
- Tag release (`vX.Y.Z`)
- Push tag (`git push origin vX.Y.Z`)
- Verify package page on Packagist

Publish Vue package to npm (automated)
--------------------------------------

[](#publish-vue-package-to-npm-automated)

This repository includes GitHub Actions workflow at `.github/workflows/publish-npm.yml`.

It publishes `@inertify/table-vue` when you push a tag `v*`.

### One-time setup

[](#one-time-setup)

1. In npm org `inertify`, create a **granular access token** with publish permissions for `@inertify/table-vue` and 2FA bypass for automation.
2. In GitHub repo settings, add secret `NPM_TOKEN` with that token value.

### Release commands

[](#release-commands)

```
npm version patch
git push origin main --follow-tags
```

The workflow validates that tag version matches `package.json` version, builds package, and publishes to npm.

Laravel API
-----------

[](#laravel-api)

```
use Inertia\Inertia;
use App\Models\User;
use Inertify\Table\Column;
use Inertify\Table\Filter;
use Inertify\Table\Table;

public function index()
{
    $table = Table::make('users')
        ->columns([
        Column::make('name')->sortable()->filterable(),
        Column::make('email')->sortable()->filterable(),
            Column::make('role')->filterable(),
        Column::make('created_at')->type('date')->sortable()->filterable(),
        ])
        ->allowedSorts(['name', 'email', 'created_at'])
      ->allowedFilters(['name', 'email', 'created_at'])
        ->defaultSort('-created_at')
        ->defaultPerPage(15)
        ->perPageOptions([15, 30, 50]);

    return Inertia::render('Users/Index', [
        ...$table->payload(
            query: User::query(),
            rowsKey: 'users',
            metaKey: 'usersTable'
        ),
    ]);
}
```

### Filter inference from column type

[](#filter-inference-from-column-type)

When `allowedFilters([...])` receives a string key, the package infers the filter type from `Column::type(...)`:

- `number` / `int` / `float` / `decimal` =&gt; `Filter::numberRange(...)`
- `date` / `datetime` / `timestamp` =&gt; `Filter::dateRange(...)`
- `boolean` / `bool` =&gt; `Filter::exact(...)`
- everything else =&gt; `Filter::partial(...)`

Use explicit `Filter::...` entries in `allowedFilters([...])` when you need custom behavior (for example `Filter::select(...)` with options or callback filters).

### Upgrade note: explicit to inferred filters

[](#upgrade-note-explicit-to-inferred-filters)

Before (explicit filters everywhere):

```
$table = Table::make('users')
  ->columns([
    Column::make('id', 'ID'),
    Column::make('name', 'Name'),
    Column::make('created_at', 'Created'),
  ])
  ->allowedFilters([
    Filter::numberRange('id', 'id', 'ID'),
    Filter::partial('name', 'name', 'Name'),
    Filter::dateRange('created_at', 'created_at', 'Created'),
  ]);
```

After (inferred defaults):

```
$table = Table::make('users')
  ->columns([
    Column::make('id', 'ID')->type('number'),
    Column::make('name', 'Name'),
    Column::make('created_at', 'Created')->type('date'),
  ])
  ->allowedFilters(['id', 'name', 'created_at']);
```

Keep using explicit `Filter::...` when you need custom filter behavior (for example `Filter::select(...)`, custom callback logic, or non-default matching rules).

### Inertia macro shortcut

[](#inertia-macro-shortcut)

The package registers a `Inertia::tablePayload(...)` macro:

```
return Inertia::render('Users/Index', [
    ...Inertia::tablePayload(
        name: 'users',
        query: User::query(),
        configure: fn ($table) => $table
            ->allowedSorts(['name', 'email'])
            ->allowedFilters([Filter::partial('name')]),
        rowsKey: 'users',
        metaKey: 'usersTable',
    ),
]);
```

Vue Headless API
----------------

[](#vue-headless-api)

### Composables-first (recommended)

[](#composables-first-recommended)

```
import {
  useTable,
  useTableFilters,
  useTableSorting,
  useTablePagination,
  useTableSelection,
} from "@inertify/table-vue";

const table = useTable(props.usersTable, {
  only: ["users", "usersTable"],
});

const filters = useTableFilters(table);
const sorting = useTableSorting(table);
const pagination = useTablePagination(table);
const selection = useTableSelection(table);
```

Each composable also supports inject fallback when used inside `HeadlessTableProvider`:

```
const filters = useTableFilters();
const sorting = useTableSorting();
const pagination = useTablePagination();
const selection = useTableSelection();
```

### Provider/inject (optional)

[](#providerinject-optional)

```

import {
  HeadlessTableProvider,
  HeadlessTableFilters,
  HeadlessTableSorting,
  HeadlessTablePagination,
} from "@inertify/table-vue";

defineProps();

```

### Direct table API

[](#direct-table-api)

```
import { useTable } from "@inertify/table-vue";

const table = useTable(props.usersTable, {
  only: ["users", "usersTable"],
});

table.toggleSort("name");
table.setFilter("role", "admin");
table.visit();

table.toggleRowSelected(1);
table.areAllRowsSelected([1, 2, 3]);
table.clearSelection();
```

### Row selection

[](#row-selection)

Use `HeadlessTableSelection` for renderless row-selection state and helpers:

```

```

Selection state is client-side and automatically clears when table meta is refreshed.

### Column-based head/cell rendering

[](#column-based-headcell-rendering)

`HeadlessTableHeads` and `HeadlessTableCells` support slot overrides by:

- Column name: `column-{key}` (example: `column-created_at`)
- Column type: `type-{type}` (example: `type-date`, `type-number`)

Precedence is: column-name slot → type slot → default slot.

Column type is resolved from `column.meta.type` first, then inferred from filter input (`date-range` =&gt; `date`, `number-range` =&gt; `number`).

### Renderless components

[](#renderless-components)

`HeadlessTable` and `HeadlessPagination` expose slot props only, so you can build any UI design system.

```

  Sort by name

  Apply

```

Example app (shadcn-vue)
------------------------

[](#example-app-shadcn-vue)

A complete usage example with Laravel + Inertia + shadcn-vue components is available in:

- `examples/laravel-vue-shadcn`

Query format
------------

[](#query-format)

For table name `users`, the default query keys are:

- `users_page`
- `users_per_page`
- `users_sort` (`name` or `-name`)
- `users_filters[name]=...`

Range filters use nested `from` / `to` values:

- `users_filters[age][from]=18`
- `users_filters[age][to]=65`
- `users_filters[created_at][from]=2026-01-01`
- `users_filters[created_at][to]=2026-01-31`

`Filter::numberRange(...)` and `Filter::dateRange(...)` apply inclusive bounds (`>= from`, `
