Admin
The Admin Shell
The optional admin shell — what installing it writes, what it reads, and the one Tailwind line without which the sidebar renders as nothing at all.
On this page
Everything an admin needs was already in the framework except the frame around
it: what is registered (Catalog), how it groups and
orders (Workspace), where each key's
page lives (ResolvesPageUrls), the pages themselves, the command palette, the
toasts. What no package shipped was a layout with a sidebar in it, so every
application wrote that part again.
wire-admin is that part, and nothing else.
Installation
composer require nyoncode/wire-adminphp artisan wire-admin:install
The installer does the three things composer require cannot, and says what it did to each:
| Step | What it writes |
|---|---|
Publishes App\Providers\WireAdminServiceProvider |
The one line that names the layout, config('livewire.component_layout') |
Writes resources/views/components/layouts/admin.blade.php |
Your layout, naming the shell component and filling its slots |
Registers the provider in bootstrap/providers.php |
So the line above runs |
Adds one @source line to resources/css/app.css |
So Tailwind compiles the classes the shell's views use |
| Publishes translations | The handful of strings the sidebar shows |
Nothing is overwritten, so a second run is safe: a layout you have edited is left alone and reported as already there. An application that keeps its providers somewhere else - Laravel 10, or its own convention - gets the line to add rather than a silent success, and the rest of the install still completes.
The One Line Tailwind Needs
That third row is the step nobody thinks of and everybody hits. Tailwind 4 finds classes by scanning your source, and it skips whatever .gitignore skips - which is vendor/, which is where every view in wire-admin, wire-panels and the modules lives. Without the line, those classes are never compiled: the sidebar renders with no width, no colour and no spacing, and nothing anywhere reports an error. The markup is correct and the stylesheet is silent about it.
The installer writes it for you, pointing at the vendor directory rather than at one package so a module you install tomorrow is covered without another edit:
@import "tailwindcss"; @source "../../vendor/nyoncode";
If You Write Your Own Layout
The shell's layout carries two things besides the markup, and a layout of your own has to carry them too:
@include('wire-core::partials.density') @include('wire-core::partials.shape')
They are the rules behind Theming → Density and
Shape. Without them the setting still renders its
attribute on <html> and nothing acts on it — a page that says it is compact
and is not, which is the same silent shape as the missing @source line above.
The attributes themselves come from the layout too:
<html data-density="{{ \NyonCode\WireCore\Foundation\Enums\Density::configured()->value }}" data-shape="{{ \NyonCode\WireCore\Foundation\Enums\Shape::configured()->value }}">
If your stylesheet is not resources/css/app.css, or does not import Tailwind, the installer says so and gives you the line rather than writing it into a file Tailwind never reads. Add it below the import, with the path adjusted to where the stylesheet lives.
Then route your pages, and they render inside the shell:
// routes/web.phpRoute::middleware(['web', 'auth'])->prefix('admin')->group(fn () => Route::wireResources());
Installing the package is still not adopting it. No provider inside wire-admin sets livewire.component_layout - running the installer is the application asking, which is a different act from having the package on disk. Skip the command and you still have the components, with your own frame free to use the sidebar alone.
How It Works
It is a separate package, and that is the opt-in. Nothing requires it; it
requires wire-panels and everything under it. An application that installs
wire-panels for its pages and its routing macro keeps getting exactly that,
with no chrome to turn off — because composer require is the switch.
Installing it does not adopt it. No provider sets livewire.component_layout.
The shell is a Blade component, so a page renders inside it only once your own
layout view says so — which also lets you keep your frame and use just the
sidebar.
It reads the seams that already existed and adds no state. The menu comes
from Workspace, every link from ResolvesPageUrls (which answers nothing at
all until a package owns routing), and the active entry from the current route
name. There is no registry here, no URL scheme and no Panel object.
The zone and the active entry are read once, while the page renders. Not per
render: inside a Livewire update Route::currentRouteName() is livewire.update,
so anything derived from it would be right on the first paint and wrong forever
after, while looking perfect. The layout renders on a full page load, which is
what makes reading it there correct rather than lucky.
Basic Usage
Write the layout your pages name, and fill its slots:
{{-- resources/views/components/layouts/admin.blade.php --}}<x-wire-admin::layout :title="$title ?? config('app.name')"> <x-slot:head> @vite(['resources/css/app.css', 'resources/js/app.js']) </x-slot:head> <x-slot:brand>{{ config('app.name') }}</x-slot:brand> <x-slot:user> <x-app-user-menu /> </x-slot:user> {{ $slot }}</x-wire-admin::layout>
Then name it once, where your application configures Livewire:
// A service provider's boot(), or config/livewire.php.// The key is `component_layout`, not `layout` — Livewire 4 reads the former,// and the latter fails as "No hint path defined for [layouts]".config()->set('livewire.component_layout', 'components.layouts.admin');
Every full-page component — including the pages Route::wireResources()
registers — now renders inside the shell, with the menu beside it.
What It Does Not Do
| Not this | Because |
|---|---|
A Panel object with a fluent API |
The risk ADR 0020 named. A class holding shell configuration is what the registries below would eventually have to know about |
| A URL scheme of its own | A zone is a route group's name(); the router owns URLs |
| A registration path | It renders what Catalog already holds, and learns nothing about what kind of thing an entry is |
| Auth, tenancy or branding config | Slots, and your own middleware |
In This Section
| Page | What it covers |
|---|---|
| The Layout | The slots, the auth frame, and the signed-in user's corner |
| The Sidebar | The menu component, the collapsed rail, and how a zone reaches it |
| Branding And Theme | The logo, the mark, the three-state theme switch, and publishing the views |
Related
- Panels — the layer whose pages render inside this shell
- Navigation — what the menu is made of
- Modules — the areas an installed package contributes to it
- Global Search — the palette the frame mounts