Rapyd Admin is an open-source admin panel for Laravel — a light alternative to Nova, Filament and Backpack, designed for teams building SaaS products and for development with AI agents.
Consistent patterns, plain code. No DSL, no proprietary resource classes. Unlike Nova or Filament, Rapyd Admin generates plain Livewire + Blade files that live in your codebase and are yours to edit. It gives you the structure you'd write anyway — consistent module layout, a unified x-rpd:: field component API, auth and multi-tenancy included — then stays out of the way.
Built for SaaS. Auth (Fortify, Socialite, 2FA), roles & permissions, and multi-tenant Companies (1–3 tiers, optional UUID keys) are bundled. One command sets up a production-ready starting point.
Agent-friendly. Flat, self-contained module folders with consistent naming. LLMs can scaffold and extend without reverse-engineering proprietary abstractions.
- PHP 8.2+
- Laravel 11 / 12 / 13
- Livewire 4
composer create-project --prefer-dist laravel/laravel myapp
cd myapp
composer require zofe/rapyd-adminRun the setup command to configure the database, publish configs, and seed the default admin user:
php artisan rpd:make:setup
php artisan serveLogin with the default admin account:
email: admin@laravel
password: admin
Rapyd Admin ships three bundled modules — no extra packages needed:
- Layout — navbar/sidebar based on SBAdmin 3, updated to Bootstrap 5.3, SCSS customizable, anonymous Blade components.
- Auth — authentication via Laravel Fortify, Socialite, 2FA, and role/permission management via
spatie/laravel-permission. - Companies — multi-tenant company hierarchy (1–3 tiers), with optional UUID primary keys.
php artisan rpd:make UserTable UserGenerates app/Livewire/UserTable.php and its blade view.
php artisan rpd:make Articles Article --module=BlogCreates app/Modules/Blog/ with:
app/Modules/Blog/
├── Livewire/
│ ├── ArticlesEdit.php
│ ├── ArticlesTable.php
│ └── ArticlesView.php
├── Views/
│ ├── articles_edit.blade.php
│ ├── articles_table.blade.php
│ └── articles_view.blade.php
└── routes.php
Datatable with filters, sorting, and pagination:
<x-rpd::table title="Articles" :items="$items">
<x-slot name="filters">
<x-rpd::input col="col-8" debounce="350" model="search" placeholder="search..." />
<x-rpd::select col="col-4" model="author_id" :options="$authors" placeholder="author..." addempty />
</x-slot>
<table class="table">
<thead><tr>
<th><x-rpd::sort model="id" label="id" /></th>
<th>title</th>
</tr></thead>
<tbody>
@foreach ($items as $article)
<tr>
<td><a href="{{ route('articles.view', $article->id) }}">{{ $article->id }}</a></td>
<td>{{ $article->title }}</td>
</tr>
@endforeach
</tbody>
</table>
</x-rpd::table>Detail page with buttons and actions:
<x-rpd::view title="Article Detail">
<x-slot name="buttons">
<x-rpd::button route="articles" color="outline-primary" label="list" />
<x-rpd::button :route="['articles.edit', $model->getKey()]" color="outline-primary" label="edit" />
</x-slot>
<div>Title: {{ $article->title }}</div>
</x-rpd::view>Form bound to a Livewire model:
<x-rpd::edit title="Article Edit">
<x-rpd::input model="article.title" label="Title" />
<x-rpd::rich-text model="article.body" label="Body" />
</x-rpd::edit>All field components use wire:model.live.debounce.150ms by default. Pass :lazy="true" to switch to wire:model.blur.
<x-rpd::input model="search" debounce="350" placeholder="search..." />
<x-rpd::select model="author_id" :options="$authors" />
<!-- TomSelect dropdown, supports remote endpoint -->
<x-rpd::select-list model="roles" multiple :options="$available_roles" label="Roles" />
<x-rpd::select-list model="roles" multiple endpoint="/ajax/roles" label="Roles" />
<x-rpd::date model="date" format="dd/MM/yyyy" value-format="yyyy-MM-dd" label="Date" />
<x-rpd::datetime model="date_time" format="dd/MM/yyyy HH:mm" value-format="yyyy-MM-dd HH:mm:ss" label="DateTime" />
<x-rpd::textarea model="body" label="Body" rows="5" />
<!-- Quill WYSIWYG -->
<x-rpd::rich-text model="body" label="Body" />
<x-rpd::upload model="file" label="Upload" />
<x-rpd::checkbox model="active" label="Active" />
<x-rpd::radiogroup model="status" :options="['active','inactive']" label="Status" />Common props: label, placeholder, model, options, debounce, prepend, append, help, icon, size, multiple, endpoint, format, value-format, rows.
<!-- Sort link inside a datatable -->
<x-rpd::sort model="id" label="id" />
<!-- Nav tabs -->
<ul class="nav nav-tabs">
<x-rpd::nav-link label="Home" route="home" />
<x-rpd::nav-link label="Articles" route="articles" />
</ul>
<!-- Sidebar with grouped items -->
<x-rpd::sidebar title="Rapyd.dev" class="p-3 text-white border-end">
<x-rpd::nav-item label="Demo" route="demo" active="/rapyd-demo" />
</x-rpd::sidebar>
<!-- Collapsible dropdown in sidebar -->
<x-rpd::nav-dropdown icon="fas fa-fw fa-book" label="KnowledgeBase" active="/kb">
<x-rpd::nav-link label="Edit Articles" route="kb.admin.articles.table" type="collapse-item" />
</x-rpd::nav-dropdown>Enable Companies in your .env:
RPD_TIERS=2
RPD_TIER1_LABEL=partner
RPD_TIER2_LABEL=customerRun php artisan rpd:make:setup — it will seed a root company and a demo tenant automatically.
To enable company scoping on your User model, run:
php artisan rpd:install --companieswire:model.lazyhas been removed in LW4. Allx-rpd::field components default towire:model.live.debounce.150ms. Pass:lazy="true"to usewire:model.blur.- Module components referenced in Blade views use
.as directory separator in the namespace:livewire:mymodule::subdir.component-name. - LW4 ships Alpine.js 3.14 internally — do not include a separate Alpine bundle.
Inspired by:
- rapyd-laravel — the original library, 900+ GitHub stars and 150k+ downloads. Rapyd Admin is its modern successor.
- livewire
- laravel-bootstrap-components

