shipbytes/laravel-ui-kit
Laravel 12+ admin panel and auth UI scaffolding with Livewire 3, Volt, Fortify, and Tailwind v4. One command installs auth pages, 2FA, admin shell, dashboard, and 7 optional modules, with a health-check doctor command.
Treat this kit as a day-one scaffold for a fresh Laravel app, not something you retrofit. The fastest path to a working admin + auth UI is:
laravel new acme && cd acme
composer require shipbytes/laravel-ui-kit
php artisan ui-kit:install
The installer is interactive: it runs a preflight scan for Breeze/Jetstream collisions, then walks a module picker (core + 9 optional modules). For CI or scripted setup, bypass the picker:
php artisan ui-kit:install --modules=admin-middleware,profile --no-interaction
Where to look first after install:
config/admin.php — this is your navigation source of truth. The sidebar/bottom-nav render from nav entries (label, route, icon, optional badge) plus ['section' => '...'] separators.config/ui-kit.php — brand name/logo/home_route for the admin shell's "back to site" link.resources/views/livewire/admin/* and app/Livewire/Admin/* — the dashboard stub and Users CRUD you'll extend.resources/views/livewire/pages/auth/* — Volt auth pages (login, register, forgot/reset, verify, confirm password).app/Models/Concerns/UiKitUser.php — generated based on your module selection; this tells you exactly which traits/methods the kit expects on User.Finish the irreducible manual wiring (the installer deliberately does not patch your app layout or User model):
// app/Models/User.php — only if admin-middleware or impersonation was picked
use App\Models\Concerns\UiKitUser;
class User extends Authenticatable { use UiKitUser; }
{{-- resources/views/layouts/app.blade.php --}}
<head><x-ui-kit::head /></head> {{-- analytics loader + dark-mode no-flash --}}
<body><x-ui-kit::banners /> {{ $slot ?? '' }}</body> {{-- impersonation ribbon --}}
// tailwind.config.js
presets: [require('shipbytes/laravel-ui-kit/tailwind-preset')],
// resources/js/app.js
import './ui-kit';
/* resources/css/app.css */
@import './ui-kit.css';
Then npm install && npm run dev, set MAIL_* in .env, and promote a test account:
php artisan tinker --execute="App\Models\User::find(1)->assignRole('admin');"
First real use case: land on /admin as an admin, see the collapsible sidebar + dashboard stub + Users CRUD, and confirm /login, /register, /profile all render under the kit layout. If that works end to end, the scaffold is healthy.
Navigation-first admin development. The kit's leverage is that the admin shell is config-driven, not view-driven. Adding a page is: create the Livewire component, add a route inside the /* ui-kit:admin-routes-* */ markers in routes/admin.php, then add one array entry to config/admin.php. Don't hand-edit sidebar Blade — you'll lose it and break the marker-based patching.
Install modules incrementally, in the order you actually ship them. php artisan ui-kit:install-module <slug> is idempotent — re-running won't duplicate nav entries or routes. Every module prints a numbered Next steps checklist because the kit intentionally does not auto-patch your route files or config/admin.php. Pattern:
config/admin.php between the markers.routes/admin.php between the markers.MAIL_* / queue / Mailable work yourself (e.g. support-tickets leaves Mailables to you, changelog leaves purifier config review to you).Sidebar badges via the resolver contract. For counters like "open tickets: 12", bind the kit's contract once in a service provider and return a keyed array:
$this->app->bind(
\Shipbytes\UiKit\Contracts\SidebarBadgeResolver::class,
\App\Support\AdminBadgeResolver::class,
);
// returns ['open_tickets' => 12, 'unread_contacts' => 3]
Keys map 1:1 to the badge field on nav items, so keep that naming stable across config/admin.php and the resolver. Cache the counts — this runs on every admin page render.
Auth customization stays in Volt. Fortify is configured with views = false, meaning Fortify handles the backend actions/validation while the kit's Volt pages own presentation. Day-to-day work goes into resources/views/livewire/pages/auth/* and app/Livewire/Forms/LoginForm.php. If you need to change validation or rate-limiting, that lives in Fortify's action classes, not in the kit's views.
Trait composition. The generated UiKitUser concern bundles whatever traits the selected modules require (Spatie Permission, impersonation, activity log, 2FA). It's a normal trait: place it alongside your own HasFactory, Notifiable, etc. If you add a module later, re-run installer and re-check that file — it regenerates based on the new selection.
Theme and sidebar state are Alpine stores. Sidebar collapse persists to localStorage; the theme toggle uses Tailwind's dark-mode class strategy. That means your own Blade views can read/update the same stores without extra JS. Use the kit's <x-theme-toggle /> and the no-flash snippet in <x-ui-kit::head /> rather than duplicating a class="dark" script.
Analytics is composable. The analytics module lets you pick UTM capture, GA4, and/or PostHog independently, and the UTM middleware is pushed into the web group at runtime — so you don't register middleware yourself. Read GOOGLE_ANALYTICS_ID / POSTHOG_* from env and let the kit's boot logic populate services.*.
Layout partials as extension points. x-ui-kit::head and x-ui-kit::banners are the intended seams. The impersonation ribbon auto-hides when no impersonation is active, so leaving it in your master layout is safe. If you have your own banners or scripts, compose them around these components rather than replacing them.
LoginForm and routes/auth.php running under the kit's layouts, which is usually subtly broken. If you must, remove the starter by hand first (the README lists exactly which files to rm) and commit before running the installer so you can diff what got generated.--no-interaction + collisions = abort. This is a safety feature, not a bug. Don't paper over it with --force in CI; start from a clean app in your test matrix.MAIL_MAILER=log makes the auth UI look healthy while reset/verification emails land in storage/logs/laravel.log. Set MAIL_FROM_ADDRESS and a real driver before you demo password reset to anyone.GOOGLE_ANALYTICS_ID and POSTHOG_* into services.google.* / services.posthog.* at boot; another tells you to add the key to config/services.php and @include a loader partial. Verify empirically after install — check config('services.google.analytics_id') in tinker — rather than trusting either paragraph.content paths are on you. The preset supplies fonts, palette, and the dark-mode class strategy; if your content array doesn't include ./app/**/*.php you'll get purged classes on Livewire components and wonder why styles vanish in production only.UI_KIT_PROMPTS_FALLBACK=1, or force fancy with =0./* ui-kit:admin-routes-* */ and /* ui-kit:nav-* */ comments. Moving entries outside them means future ui-kit:install-module runs can't keep things tidy and idempotent.git status before each module install is the cheapest possible safety net, and it lets you review generated code — which you should do anyway, since you're adopting it as your scaffold.composer.json, read every generated file you commit, and don't assume upstream fixes will arrive. Treat the kit as a one-time generator you now own, not a runtime dependency you can lean on indefinitely.lab404/laravel-impersonate is installed by the module, but the login-as button partial and exit ribbon do nothing until UiKitUser is on your User model. Same class of failure applies to Spatie roles via admin-middleware — it falls back to the is_admin boolean if you skip the module, which is fine but easy to forget when an authorization check "works" in one environment and not another.Shipbytes\UiKit\Contracts\SidebarBadgeResolver (dynamic counters), config/admin.php (nav/guard/middleware), config/ui-kit.php (brand + home_route), and the x-ui-kit::head / x-ui-kit::banners components (layout seams). Almost everything else is code you generated and can edit freely.How can I help you explore Laravel packages today?