Admin Menus
Define navigation items in the admin dashboard via plugin.json.
Menu Structure
{
"menus": [
{
"id": "my-plugin-main",
"label": "My Plugin",
"icon": "icon-plugin",
"position": 25,
"url": "/admin/my-plugin",
"capability": "manage_plugin",
"children": [
{
"id": "my-plugin-settings",
"label": "Settings",
"url": "/admin/my-plugin/settings",
"capability": "manage_plugin_settings"
},
{
"id": "my-plugin-logs",
"label": "Logs",
"url": "/admin/my-plugin/logs",
"capability": "view_plugin_logs"
}
]
}
]
}
Menu Properties
| Property | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique menu identifier |
label | string | Yes | Display text |
icon | string | No | Icon class (CSS class) |
position | integer | No | Menu order (lower = higher) |
url | string | No | Menu link URL |
capability | string | No | Required capability to view |
children | array | No | Submenu items |
Icon Classes
Use standard icon classes (e.g., from Font Awesome or Bootstrap Icons):
icon-settings (gear/cog)
icon-dashboard (chart)
icon-users (people)
icon-file (document)
icon-cog (wrench)
icon-plugin (puzzle piece)
Dynamic Menus
Register menus programmatically in your service provider:
<?php
namespace MyVendor\MyPlugin\Providers;
use Illuminate\Support\ServiceProvider;
class PluginServiceProvider extends ServiceProvider
{
public function boot()
{
// Register menu items dynamically
add_filter('fundorex:admin.menus', function($menus) {
$menus[] = [
'id' => 'my-plugin-main',
'label' => 'My Plugin',
'icon' => 'icon-plugin',
'url' => '/admin/my-plugin',
'capability' => 'manage_plugin',
];
return $menus;
});
}
}
Static Assets
Define CSS and JavaScript files in plugin.json.
Asset Configuration
{
"assets": {
"css": [
"resources/assets/css/style.css"
],
"js": [
"resources/assets/js/app.js",
"resources/assets/js/dashboard.js"
]
}
}
Asset Paths
- Paths are relative to plugin root directory
- Assets are copied to
public/plugins/{plugin-name}/during publication - Use
asset('plugins/my-plugin/css/style.css')in views
Publishing Assets
Publish plugin assets via command:
php artisan vendor:publish --tag=my-plugin-assets
Or in your service provider:
public function boot()
{
$this->publishes([
__DIR__ . '/../../resources/assets' => public_path('plugins/my-plugin'),
], 'my-plugin-assets');
}
Asset Linking in Views
<link rel="stylesheet" href="{{ asset('plugins/my-plugin/css/style.css') }}">
<script src="{{ asset('plugins/my-plugin/js/app.js') }}"></script>
Versioning Assets
For cache busting, version assets:
<link rel="stylesheet"
href="{{ asset('plugins/my-plugin/css/style.css?v=' . $plugin->getVersion()) }}">
Routes
Define plugin routes in routes/web.php.
Basic Routes
<?php
use Illuminate\Support\Facades\Route;
Route::prefix('admin/my-plugin')->middleware(['auth', 'admin'])->group(function () {
Route::get('/', 'DashboardController@index')->name('dashboard');
Route::get('/settings', 'SettingsController@index')->name('settings');
Route::post('/settings', 'SettingsController@store')->name('settings.store');
});
Route Attributes
Define route attributes in plugin.json:
{
"routes": {
"web": "routes/web.php",
"api": "routes/api.php"
}
}
API Routes
<?php
use Illuminate\Support\Facades\Route;
Route::prefix('api/v1/my-plugin')->middleware(['api', 'auth:api'])->group(function () {
Route::get('/campaigns', 'API\\CampaignController@index');
Route::post('/campaigns', 'API\\CampaignController@store');
Route::get('/campaigns/{id}', 'API\\CampaignController@show');
});
Route Parameters
Route::get('/campaigns/{campaign}', function(Campaign $campaign) {
return view('my-plugin::campaign.show', ['campaign' => $campaign]);
});
Route Groups
Route::group([
'prefix' => 'admin/my-plugin',
'middleware' => ['auth', 'admin'],
'as' => 'my-plugin.',
], function () {
Route::get('/', 'DashboardController@index')->name('dashboard');
Route::resource('campaigns', 'CampaignController');
});
View Blade Templates
Store templates in resources/views/. Load in service provider:
public function boot()
{
$this->loadViewsFrom(
__DIR__ . '/../../resources/views',
'my-plugin'
);
}
Using Views
return view('my-plugin::dashboard');
return view('my-plugin::campaign.show', ['campaign' => $campaign]);
View Inheritance
@extends('layouts.admin')
@section('title', 'My Plugin')
@section('content')
<div class="plugin-content">
<!-- Your content -->
</div>
@endsection
JavaScript & Vue Components
If your plugin uses frontend frameworks:
Vue 3 Setup
// resources/assets/js/app.js
import { createApp } from 'vue';
import MyComponent from './components/MyComponent.vue';
const app = createApp({
components: {
MyComponent,
},
});
app.mount('#app');
Alpine.js Setup
<script src="{{ asset('plugins/my-plugin/js/alpine.min.js') }}" defer></script>
<div x-data="pluginApp()">
<button @click="openModal">Settings</button>
</div>
<script src="{{ asset('plugins/my-plugin/js/app.js') }}"></script>
Best Practices
Asset Organization
resources/
├── assets/
│ ├── css/
│ │ ├── app.css
│ │ └── admin.css
│ └── js/
│ ├── app.js
│ └── utils.js
└── views/
├── dashboard/
├── settings/
└── layouts/
Menu Icon Usage
Use consistent icon classes across your plugin. Define a custom icon set if needed.
Route Security
Always protect admin routes with authentication and capability checks:
Route::middleware(['auth', 'admin', 'capability:manage_plugin'])->group(function () {
// Protected routes
});
Asset Caching
Set proper cache headers in production:
header('Cache-Control: public, max-age=31536000, immutable');
Next Steps
- Permissions, Settings & Data — Permissions and data management
- Hooks Reference — Available hooks
Still stuck?
Our support team is ready to help you get set up.

