Navigation
Every panel has a sidebar on the left and a topbar at the top. Anvil builds both from the panel, its pages and resources; you describe labels, icons, groups and order, not the HTML. Entries the user may not open are left out.
┌──────────────┬──────────────────────────────────────────────────────────┐│ ▣ Brand │ ⌂ › Invoices › INV-2026-0042 🔍 Search ⌘K 🔔 🏢 ◐ (RK) ││ ├──────────────────────────────────────────────────────────┤│ ⌂ Dashboard │ ││ ☑ Inbox 3 │ ││ SALES │ page content ││ ▤ Customers │ ││ ▤ Invoices │ ││ ▸ SETTINGS │ ││ « │ │└──────────────┴──────────────────────────────────────────────────────────┘| Part | What it shows | Configured in | Page |
|---|---|---|---|
| Sidebar | one entry per page and resource the user may open | page / resource | Sidebar |
| Groups | headings and collapsible sections of the sidebar | Panel::navigationGroups() |
Groups |
| Badges | a count or short text next to an entry | navigationBadge() |
Badges |
| User menu | profile, own entries, logout | Panel::userMenuItems() |
User menu |
| Topbar | breadcrumbs, global search, notifications, tenant, theme | page, resources, panel | Topbar and breadcrumbs |
A complete example
Section titled “A complete example”use Anvil\Panel\Navigation\NavigationGroup;use Anvil\Panel\Navigation\UserMenuItem;use Anvil\Panel\Panel;
final class AdminPanel extends Panel{ public function id(): string { return 'admin'; }
public function resources(): array { return [CustomerResource::class, InvoiceResource::class, UserResource::class]; }
public function pages(): array { return [ReportsPage::class, SettingsPage::class]; }
public function navigationGroups(): array { return [ 'Sales', NavigationGroup::make('Reports')->icon('bar-chart')->collapsible(), NavigationGroup::make('Settings')->icon('gear')->collapsed(), ]; }
public function userMenuItems(): array { return [UserMenuItem::page('settings')]; }}
final class InvoiceResource extends Resource{ protected string $navigationIcon = 'receipt'; protected ?string $navigationGroup = 'Sales'; protected int $navigationSort = 10;
public function navigationBadge(): ?string { $overdue = $this->query()->where('status', 'overdue')->countAllResults();
return $overdue > 0 ? (string) $overdue : null; }}Principles
Section titled “Principles”- Derived, not declared: there is no separate menu definition. A new resource or page appears in the sidebar as soon as it is registered in the panel.
- Permissions decide: an entry is shown only if the user may open its target – the same check that protects the page. Hiding an entry never replaces that check.
- Escaped: labels, badges and group names are escaped; icons are
Bootstrap icon names without
bi-. - Translations: labels are plain strings; return
lang('…')for multilingual applications, see Extending Anvil. - Remembered in the browser: the collapsed sidebar and collapsed
groups are stored in the local storage of the browser, the theme in a
cookie (
anvil_theme); nothing of it on the server.