PHPackages                             tbtop/admin - 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. tbtop/admin

ActiveLibrary[Admin Panels](/categories/admin)

tbtop/admin
===========

Inertia admin builder - PHP DSL pages rendered by the @tbtop React client

v0.2.31(2w ago)0737↑180%[2 issues](https://github.com/DiVotek/tbtop/issues)[1 PRs](https://github.com/DiVotek/tbtop/pulls)MITTypeScriptPHP ^8.4CI passing

Since Jun 15Pushed 1w agoCompare

[ Source](https://github.com/DiVotek/tbtop)[ Packagist](https://packagist.org/packages/tbtop/admin)[ Docs](https://github.com/tbtop/admin)[ RSS](/packages/tbtop-admin/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (10)Dependencies (6)Versions (34)Used By (0)

Tabletop — Laravel + Inertia admin builder
==========================================

[](#tabletop--laravel--inertia-admin-builder)

An admin builder: pages are authored in a PHP DSL, serialized to a JSON structure in Inertia props, and rendered by a React interpreter. Filament's authoring model, without Livewire.

Layout
------

[](#layout)

PackageWhat it is`packages/php`composer `tbtop/admin` — DSL, controllers, Effects, nav, uploads`packages/client`npm `@tbtop/inertia-admin` — render layer + Inertia integration`packages/contracts`JSON Schema grammar + kitchen-sink fixture (the contract shared by both sides)`apps/demo`Laravel 12 + Inertia v3 reference app (acceptance)Run the demo
------------

[](#run-the-demo)

```
cd apps/demo
composer install && npm install
php artisan migrate --seed && php artisan storage:link
php artisan serve --port=8090   # + npm run dev (vite)
# http://127.0.0.1:8090/admin/posts — admin@admin.com / password
```

A page in 30 seconds
--------------------

[](#a-page-in-30-seconds)

```
class PostsIndexPage extends Page
{
    public static function path(): string { return 'posts'; }

    public static function nav(): ?array
    {
        return ['group' => 'Content', 'label' => 'Posts', 'order' => 1];
    }

    public function view(S $s): Node
    {
        return $s->stack([
            $s->table('posts')
                ->columns(['title' => 'Title', 'views' => 'Views'])
                ->searchable(['title'])
                ->defaultSort('created_at', 'desc')
                ->query(fn () => Post::query())
                ->rowActions([
                    $s->action('edit')->label('Edit')
                        ->visit('/admin/posts/{row.id}/edit'),   // row template
                    $s->action('delete')->label('Delete')->color('danger')
                        ->confirm('Delete this post?')
                        ->handle(function (ActionCtx $ctx): Effects {
                            Post::whereKey($ctx->row['id'])->delete();
                            return Effects::make()->notify('Deleted')->refreshTable();
                        }, needs: ['row']),
                ])
                ->toNode(),
        ]);
    }
}
```

Registration: add the class to `config/tbtop-admin.php` → `'pages'`. Routes and the table/data/form/action endpoints are wired automatically under the configured `prefix` + `middleware`.

Forms
-----

[](#forms)

```
$s->form('post', [
    $s->text('title')->label('Title')->required()->rules('max:200'),
    $s->translatable('intro')->set('locales', ['en', 'uk']),
    $s->repeater('sections')->rules('array|max:10')->set('fields', [
        $s->text('heading')->required(),
    ]),
    $s->actionsRow([
        $s->action('save')->label('Save')->keybinding('mod+s')->submit(),
    ]),
])
->record($post->toArray())                       // initial data → props
->onSubmit(function (ActionCtx $ctx): Effects {  // $ctx->form = validated
    $post->update($ctx->form);
    return Effects::make()->notify('Saved');
});
```

- Laravel owns validation: rules are collected from the fields (repeater → `parent.*.child`), `validate()` → 422 → errors land on the fields. Regex rules must use the array form.
- The declarative subset of rules ships to the client as `constraints` for on-blur validation.
- Submit goes through Inertia `router.post` (errors bag, history); success effects arrive via flash.
- A field with no rules gets a baseline `nullable` (otherwise Laravel drops it from the validated payload).

Actions — five kinds
--------------------

[](#actions--five-kinds)

SpecWhat it does`->visit(url)`Inertia visit; supports `{row.id}` templates`->submit()`submit the nearest (or a named) form`->handle(fn, needs: [...])`POST to a server closure; payload by `needs`: form/row/selection`->modal(title, $node)`client modal with a StructureNode body`->custom('name', params)`client registry via `defineCustomAction()``->confirm(title)` wraps a server action in a confirm modal. Server closures resolve by name per-request — they never travel over the wire.

**Effects** (a closed set): `notify | redirect | refreshTable | resetForm | closeModal`. Extending the set is a minor contract bump; anything non-standard goes through `custom`.

Uploads
-------

[](#uploads)

Profiles live in config (`'uploads' => ['media' => [disk, dir, accept, maxSize, sizes]]`), the endpoint is `POST {prefix}/uploads/{profile}`, with GD fit-inside variants. The field `$s->upload('file')->set('entity', 'media')` delivers a full UploadRow to `$ctx->form['file']` (url, mimeType, filesize, width/height, sizes).

The contract
------------

[](#the-contract)

`contracts/structure.schema.json` is the wire grammar. Gates:

- PHP: the kitchen-sink page validates against the schema + a snapshot (`UPDATE_FIXTURES=1 vendor/bin/pest` to regenerate);
- client: the same fixture passes the zod mirror and a render smoke test.

A new block = update the schema + the zod mirror + the fixture in one PR.

Quality gates
-------------

[](#quality-gates)

```
cd packages/php && vendor/bin/pest && vendor/bin/phpstan analyse && vendor/bin/pint --test
cd packages/client && bun test && bunx tsc --noEmit
cd apps/demo && php artisan test
```

phpstan runs at level 5 (the skeleton default; raise it in `phpstan.neon.dist`).

Status
------

[](#status)

See `docs/roadmap.md` for the release plan and the current gap list (auth-page layout and the relation field are the known blockers). Per-package contributor notes live in the root `CLAUDE.md`.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance98

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 82.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

32

Last Release

14d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/17353886?v=4)[DiVotek](/maintainers/divotek)[@DiVotek](https://github.com/DiVotek)

---

Top Contributors

[![MaxBoiko21](https://avatars.githubusercontent.com/u/92517114?v=4)](https://github.com/MaxBoiko21 "MaxBoiko21 (193 commits)")[![TsukuroTadzaki](https://avatars.githubusercontent.com/u/40655949?v=4)](https://github.com/TsukuroTadzaki "TsukuroTadzaki (41 commits)")

---

Tags

laraveladminTbtop

### Embed Badge

![Health badge](/badges/tbtop-admin/health.svg)

```
[![Health](https://phpackages.com/badges/tbtop-admin/health.svg)](https://phpackages.com/packages/tbtop-admin)
```

###  Alternatives

[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M48](/packages/spatie-laravel-pdf)[filament/support

Core helper methods and foundation code for all Filament packages.

2331.0M273](/packages/filament-support)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[mradder/filament-logger

Audit logging, activity tracking, exports, alerts, and dashboards for Filament admin panels.

2318.8k](/packages/mradder-filament-logger)[lettermint/lettermint-laravel

Official Lettermint driver for Laravel

1190.2k1](/packages/lettermint-lettermint-laravel)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
