offload-project/laravel-navigation
Composer 安装命令:
composer require offload-project/laravel-navigation
包简介
Configurable navigation package for Laravel with breadcrumb and tree generation
README 文档
README
Laravel Navigation
A powerful, flexible navigation management package for Laravel. Define multiple navigation structures with breadcrumbs, active state detection, and pre-compiled icons — perfect for Inertia.js, React, Vue, and Blade applications.
Features
- Multiple Navigations — Define unlimited nav structures (main, footer, sidebar, user menu)
- Fluent Builder API — IDE-friendly builder with full autocomplete support
- Sections & Groups — Organize items with top-level sections and collapsible groups
- Route-Based — Use Laravel route names with full IDE autocomplete
- Breadcrumb Generation — Auto-generate breadcrumbs from your navigation config
- Active State Detection — Smart detection of active items and their parents
- Authorization — Built-in
canandvisibleattributes for permissions - Pre-compiled Icons — Compile Lucide icons to inline SVG for optimal performance
- Action Support — POST/DELETE actions for logout, form submissions, etc.
- Wildcard Parameters — Match dynamic routes like
/users/{id}/editin breadcrumbs - Custom Metadata — Attach badges, feature flags, or any data to nav items
Table of Contents
- Requirements
- Installation
- Quick Start
- Runtime Registration
- Getting Navigation Data
- Fluent Builder API
- Groups & Sections
- Route Parameters
- Authorization
- Action Items
- Breadcrumbs & Wildcards
- Visibility Control
- Custom Metadata
- Icon Compilation
- Route Validation
- Inertia.js Integration
- Output Format
- Configuration Reference
- Error Messages
- Migration Guide
- AI Coding Assistant Skill
- Testing
- Contributing
- Security
- License
Requirements
- PHP 8.3+
- Laravel 11/12/13
Installation
composer require offload-project/laravel-navigation
Optionally publish the configuration:
php artisan vendor:publish --tag=navigation-config
Quick Start
Using the Fluent Builder (Recommended)
The fluent builder provides full IDE autocomplete and a clean, readable syntax:
use OffloadProject\Navigation\Item; return [ 'navigations' => [ 'main' => [ Item::make('Dashboard')->route('dashboard')->icon('home'), Item::make('Users') ->route('users.index') ->icon('users') ->can('view-users') ->children([ Item::make('All Users')->route('users.index'), Item::make('Roles')->route('roles.index'), ]), Item::make('Settings')->route('settings')->icon('settings'), ], ], ];
Using Helper Functions
Global helper functions provide the most concise syntax:
return [ 'navigations' => [ 'main' => [ nav_item('Dashboard', 'dashboard', 'home'), nav_item('Users', 'users.index', 'users') ->can('view-users') ->children([ nav_item('All Users', 'users.index'), nav_item('Roles', 'roles.index'), ]), nav_separator(), nav_item('Settings', 'settings', 'settings'), nav_external('Documentation', 'https://docs.example.com', 'book'), nav_action('Logout', 'logout', 'post', 'log-out'), ], ], ];
Available helpers:
nav_item($label, $route?, $icon?)— Standard navigation itemnav_group($label, $children?, $icon?)— Collapsible groupnav_section($label, $children?, $icon?)— Top-level section containing items and groupsnav_separator()— Visual separatornav_divider($spacing?)— Divider with optional spacingnav_external($label, $url, $icon?)— External linknav_action($label, $route, $method?, $icon?)— POST/DELETE action
Using Shorthand Syntax
For quick configuration, use the shorthand array syntax:
return [ 'navigations' => [ 'main' => [ ['Dashboard', 'dashboard', 'home'], // [label, route, icon] ['Users', 'users.index', 'users'], // [label, route, icon] ['Docs', 'https://docs.example.com', 'book'], // URLs auto-detected ], ], ];
Using Array Syntax
The traditional array syntax is still fully supported:
return [ 'navigations' => [ 'main' => [ ['label' => 'Dashboard', 'route' => 'dashboard', 'icon' => 'home'], [ 'label' => 'Users', 'route' => 'users.index', 'icon' => 'users', 'can' => 'view-users', 'children' => [ ['label' => 'All Users', 'route' => 'users.index'], ['label' => 'Roles', 'route' => 'roles.index'], ], ], ], ], ];
Runtime Registration
Register navigations at runtime in your service provider or middleware:
use OffloadProject\Navigation\Facades\Navigation; use OffloadProject\Navigation\Item; // Fluent builder Navigation::register('sidebar') ->item('Dashboard', 'dashboard', 'home') ->item('Users', 'users.index', 'users') ->child('All Users', 'users.index') ->child('Create User', 'users.create') ->separator() ->section('Admin', [ Item::make('Roles')->route('admin.roles'), Item::group('Settings', [ Item::make('General')->route('settings.general'), ]), ], 'shield') ->item('Settings', 'settings', 'settings') ->done(); // Or with Item instances Navigation::addNavigation('sidebar', [ Item::make('Dashboard')->route('dashboard')->icon('home'), Item::make('Users')->route('users.index')->icon('users'), ]);
Getting Navigation Data
use OffloadProject\Navigation\Facades\Navigation; // Get navigation items $items = Navigation::get('main')->items(); // Get breadcrumbs (auto-detects current route) $breadcrumbs = Navigation::breadcrumbs('main'); // Check if navigation exists if (Navigation::has('sidebar')) { // ... } // Get all navigation names $names = Navigation::names();
Pass to your frontend:
// Inertia.js return inertia('Dashboard', [ 'navigation' => Navigation::get('main')->items(), 'breadcrumbs' => Navigation::breadcrumbs('main'), ]);
Fluent Builder API
The Item class provides a fluent interface with full IDE support:
use OffloadProject\Navigation\Item; // Basic item Item::make('Dashboard')->route('dashboard')->icon('home') // Shorthand with route and icon Item::to('Dashboard', 'dashboard', 'home') // External link Item::external('Documentation', 'https://docs.example.com', 'book') // Action (POST/DELETE) Item::action('Logout', 'logout', 'post', 'log-out') // Separator Item::separator() // Divider with spacing Item::divider('large') // Group (collapsible sub-menu) Item::group('Settings', [ Item::make('Profile')->route('settings.profile'), ]) // Section (top-level container for items and groups) Item::section('Workspace', [ Item::make('Dashboard')->route('dashboard'), Item::group('Settings', [...]), ]) // With children Item::make('Settings') ->route('settings') ->icon('settings') ->children([ Item::make('Profile')->route('settings.profile'), Item::make('Security')->route('settings.security'), ]) // With authorization Item::make('Admin') ->route('admin') ->can('access-admin') // With visibility Item::make('Dashboard') ->route('dashboard') ->visible(fn () => auth()->check()) ->whenAuthenticated() // Shorthand for authenticated users ->whenGuest() // Shorthand for guests only // With badge Item::make('Notifications') ->route('notifications') ->badge(5) ->badge(fn () => auth()->user()->unreadCount(), 'red') // For breadcrumbs only Item::make(fn ($user) => "Edit: {$user->name}") ->route('users.edit') ->params(['user' => '*']) ->breadcrumbOnly() // For navigation only Item::make('Admin Section') ->route('admin') ->navOnly() // Custom metadata Item::make('Dashboard') ->route('dashboard') ->meta('badge', 5) ->meta('feature', 'beta')
Groups & Sections
Use groups for collapsible sub-menus and sections for top-level structural containers that can hold both items and groups.
Groups
Organize navigation items into collapsible groups with headers:
use OffloadProject\Navigation\Item; return [ 'navigations' => [ 'sidebar' => [ Item::make('Dashboard')->route('dashboard')->icon('home'), Item::group('Settings', [ Item::make('Profile')->route('settings.profile'), Item::make('Security')->route('settings.security'), Item::make('Notifications')->route('settings.notifications'), ])->icon('cog'), Item::group('Administration', [ Item::make('Users')->route('admin.users'), Item::make('Roles')->route('admin.roles'), ])->icon('shield')->collapsed(), // Starts collapsed ], ], ];
Or with helper functions:
return [ 'navigations' => [ 'sidebar' => [ nav_item('Dashboard', 'dashboard', 'home'), nav_group('Settings', [ nav_item('Profile', 'settings.profile'), nav_item('Security', 'settings.security'), ], 'cog'), nav_group('Admin', [], 'shield') ->collapsed() ->can('access-admin') ->children([ nav_item('Users', 'admin.users'), nav_item('Roles', 'admin.roles'), ]), ], ], ];
Group Options
// Basic group Item::group('Settings', [...]) // With icon Item::group('Settings', [...])->icon('cog') // Not collapsible (always expanded) Item::group('Main')->collapsible(false) // Starts collapsed Item::group('Advanced')->collapsed() // With authorization Item::group('Admin')->can('access-admin') // With visibility Item::group('Beta')->visible(config('features.beta')) // Nested groups Item::group('Settings', [ Item::make('General')->route('settings.general'), Item::group('Advanced', [ Item::make('API Keys')->route('settings.api'), Item::make('Webhooks')->route('settings.webhooks'), ])->collapsed(), ])
Group Output
Groups output with these additional fields:
[
'id' => 'nav-sidebar-1',
'label' => 'Settings',
'url' => null, // Groups don't have URLs
'isActive' => true, // Active if any child is active
'icon' => '<svg>...</svg>',
'group' => true, // Identifies this as a group
'collapsible' => true, // Can be collapsed
'collapsed' => false, // Default collapsed state
'children' => [...],
]
Sections
Sections are top-level structural containers that can hold both items and groups. They support all the same fluent options as groups but default to non-collapsible so they read as structural dividers (e.g., WORKSPACE, ADMIN).
use OffloadProject\Navigation\Item; return [ 'navigations' => [ 'sidebar' => [ Item::section('Workspace', [ Item::make('Dashboard')->route('dashboard')->icon('home'), Item::group('Settings', [ Item::make('Profile')->route('settings.profile'), Item::make('Security')->route('settings.security'), ])->icon('cog'), ])->icon('layers'), Item::section('Admin', [ Item::make('Users')->route('admin.users'), Item::make('Roles')->route('admin.roles'), ])->can('access-admin'), ], ], ];
Or with helper functions:
return [ 'navigations' => [ 'sidebar' => [ nav_section('Workspace', [ nav_item('Dashboard', 'dashboard', 'home'), nav_group('Settings', [ nav_item('Profile', 'settings.profile'), ], 'cog'), ], 'layers'), ], ], ];
Or via the runtime builder:
Navigation::register('sidebar') ->section('Workspace', [ Item::make('Dashboard')->route('dashboard'), Item::group('Settings', [ Item::make('Profile')->route('settings.profile'), ]), ]) ->done();
Section Options
Sections support every option groups support — icons, gates, badges, custom meta, visibility, and (opt-in) collapsibility:
// Default: non-collapsible structural divider Item::section('Workspace', [...]) // Make it collapsible (and optionally start collapsed) Item::section('Advanced', [...])->collapsible()->collapsed() // With icon and authorization Item::section('Admin', [...])->icon('shield')->can('access-admin') // With a badge Item::section('Notifications', [...])->badge(3)
Nesting Rules
Sections are top-level only. Nesting a section inside another section or inside a group throws InvalidNavigationItemException:
// Not allowed — sections cannot be nested Item::section('Outer', [ Item::section('Inner', [...]), // throws ]) // Not allowed — groups cannot contain sections Item::group('Settings', [ Item::section('Inner', [...]), // throws ])
Section Output
Sections output with these additional fields:
[
'id' => 'nav-sidebar-0',
'label' => 'Workspace',
'isActive' => true, // Active if any child is active
'icon' => '<svg>...</svg>',
'section' => true, // Identifies this as a section
'collapsible' => false, // Defaults to false (opt in via ->collapsible())
'collapsed' => false,
'children' => [...], // Items and/or groups
]
Route Parameters
Pass parameters for routes that require them:
// For routes like /users/{user}/posts $items = Navigation::get('sidebar')->items(['user' => $user->id]);
Authorization
Use the can attribute to check gates or policies:
Item::make('Admin') ->route('admin.index') ->can('access-admin') // With policy model Item::make('Edit Post') ->route('posts.edit') ->can(['update', $post])
Items are automatically hidden when the user isn't authenticated or lacks permission.
For non-authorization logic (feature flags, environment checks), use visible:
Item::make('Beta Features') ->route('beta') ->visible(config('features.beta')) // Or with closures Item::make('Debug') ->route('debug') ->visible(fn () => app()->isLocal())
Action Items
Define items that trigger POST/DELETE requests:
Item::action('Logout', 'logout', 'post', 'log-out') // Or with array syntax ['label' => 'Logout', 'route' => 'logout', 'method' => 'post', 'icon' => 'log-out']
Handle in your frontend by checking for the method key and using a form or Inertia's router.post().
Breadcrumbs & Wildcards
Handle CRUD pages elegantly with breadcrumbOnly and wildcard parameters:
Item::make('Users') ->route('users.index') ->children([ Item::make(fn ($user) => "Edit: {$user->name}") ->route('users.edit') ->params(['user' => '*']) ->breadcrumbOnly(), ])
When visiting /users/5/edit:
- Navigation shows only "Users"
- Breadcrumbs show "Users > Edit: John Doe"
- Active state marks "Users" as active
Breadcrumbs API
// Auto-detect current route, search all navigations $breadcrumbs = Navigation::breadcrumbs(); // Search specific navigation $breadcrumbs = Navigation::breadcrumbs('main'); // Specify route explicitly $breadcrumbs = Navigation::breadcrumbs('main', 'users.edit'); // With route parameters $breadcrumbs = Navigation::breadcrumbs('main', 'users.edit', ['user' => $user]);
Visibility Control
Use navOnly and breadcrumbOnly to control where items appear:
Item::make('Admin Section') ->route('admin.index') ->navOnly() // Shows in nav, excluded from breadcrumbs ->children([ Item::make('Users')->route('admin.users'), Item::make(fn ($user) => "Edit {$user->name}") ->route('admin.users.edit') ->params(['user' => '*']) ->breadcrumbOnly(), // Shows in breadcrumbs, excluded from nav ])
navOnly— Section headers that would be redundant in breadcrumbsbreadcrumbOnly— Edit/show pages that shouldn't clutter navigation
Custom Metadata
Attach any data to navigation items:
Item::make('Notifications') ->route('notifications') ->badge(5) ->meta('badgeColor', 'red') ->meta('feature', 'new') // Or with array syntax [ 'label' => 'Notifications', 'route' => 'notifications', 'badge' => 5, 'badgeColor' => 'red', ]
Custom keys pass through unchanged to your frontend.
Icon Compilation
Pre-compile Lucide icons to inline SVG for optimal performance:
php artisan navigation:compile-icons
Add to your deployment pipeline for production builds.
Route Validation
Validate all route references exist:
php artisan navigation:validate
Add to CI/CD to catch broken navigation links early.
Inertia.js Integration
Share navigation globally via middleware:
// app/Http/Middleware/HandleInertiaRequests.php public function share(Request $request): array { return [ ...parent::share($request), 'navigation' => Navigation::get('main')->items(), 'breadcrumbs' => Navigation::breadcrumbs('main'), ]; }
Output Format
The items() method returns a frontend-ready structure:
[
[
'id' => 'nav-main-0',
'label' => 'Dashboard',
'url' => '/dashboard',
'isActive' => true,
'icon' => '<svg>...</svg>',
'children' => [],
],
// ...
]
The breadcrumbs() method returns:
[
['id' => 'breadcrumb-main-0-1', 'label' => 'Users', 'route' => 'users.index', 'url' => '/users'],
['id' => 'breadcrumb-main-1-0', 'label' => 'Edit: John', 'route' => 'users.edit', 'url' => '/users/1/edit', 'isActive' => true],
]
The last breadcrumb includes isActive => true to identify the current page.
Configuration Reference
| Option | Type | Description |
|---|---|---|
label |
string|Closure |
Display text (closures receive route models) |
route |
string |
Laravel route name |
url |
string |
External URL (alternative to route) |
method |
string |
HTTP method (post, delete) |
icon |
string |
Lucide icon name |
children |
array |
Nested navigation items |
visible |
bool|Closure |
Visibility condition |
can |
string|array |
Gate/policy check |
breadcrumbOnly |
bool |
Hide from nav, show in breadcrumbs |
navOnly |
bool |
Show in nav, hide from breadcrumbs |
params |
array |
Route parameters (['id' => '*'] for wildcards) |
Error Messages
The package provides helpful error messages when configuration is invalid:
Navigation item cannot have both "route" and "url". Use "route" for internal
Laravel routes (e.g., "users.index") or "url" for external links
(e.g., "https://docs.example.com"), but not both.
See: https://github.com/offload-project/laravel-navigation#routing
Migration Guide
Upgrading to v1.1
Breaking Changes
Icon Storage Format Changed
Compiled icons are now stored as JSON instead of PHP. If you have previously compiled icons:
# Recompile your icons to use the new format
php artisan navigation:compile-icons
The old PHP format (storage/navigation/icons.php) will still be loaded for backwards compatibility, but new compilations will use JSON (storage/navigation/icons.json).
NavigationManager Constructor
If you're manually instantiating NavigationManager, the constructor now requires an ItemVisibilityResolver instance:
// Before new NavigationManager($config, $iconCompiler); // After new NavigationManager($config, $iconCompiler, $visibilityResolver);
Most users won't be affected as the class is typically resolved from the container.
Deprecated Methods
The following methods are deprecated and will be removed in v2.0:
| Deprecated | Use Instead |
|---|---|
->toTree() |
->items() |
->getBreadcrumbs() |
->breadcrumbs() |
AI Coding Assistant Skill
This package ships a Laravel Boost skill so coding assistants (Claude Code, Cursor, etc.) follow the package's conventions when generating code. Install it in your app with:
php artisan boost:add-skill offload-project/laravel-navigation
The skill source lives at skills/SKILL.md.
Testing
composer test
Contributing
Contributions are welcome! Please see the documents below before getting started.
- Contributing Guide — setup, workflow, commit conventions, and PR process
- Code of Conduct — expectations for participation in this project
Security
- Security Policy — how to report a vulnerability privately
License
The MIT License (MIT). Please see License File for more information.
offload-project/laravel-navigation 适用场景与选型建议
offload-project/laravel-navigation 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 2.14k 次下载、GitHub Stars 达 4, 最近一次更新时间为 2025 年 12 月 16 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「navigation」 「laravel」 「nav」 「wayfinder」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 offload-project/laravel-navigation 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 offload-project/laravel-navigation 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 offload-project/laravel-navigation 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Miaoxing Nav Plugin
A block to display a list of links to child pages, or pages in current level
Yii2 asset collection of useful JavaScript libraries
Pop Nav Component for Pop PHP Framework
Rinvex Menus is a simple menu builder package for Laravel, that supports hierarchical structure, ordering, and styling with full flexibility using presenters for easy styling and custom structure of menu rendering.
Get wordpress nav menus and share the main site menus with the child sites.
统计信息
- 总下载量: 2.14k
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 4
- 点击次数: 31
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-12-16