Originally published on laravel.learnio.dev — part of the Laravel best practices course. View this lesson on the original site.
Organise related routes once, not repeatedly
Route groups let you share middleware, URI prefixes, and name prefixes across multiple routes.
Group routes with shared middleware
use Illuminate\Support\Facades\Route;
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', fn () => 'Dashboard')->name('dashboard');
Route::get('/account', fn () => 'Account')->name('account');
});
This avoids repeating ->middleware('auth') on every route.
Add URI and name prefixes
Route::middleware(['auth'])
->prefix('admin')
->name('admin.')
->group(function () {
Route::get('/users', fn () => 'Users')->name('users');
Route::get('/reports', fn () => 'Reports')->name('reports');
});
Result:
/admin/usersnamedadmin.users/admin/reportsnamedadmin.reports
Nested groups in real projects
Route::prefix('admin')->name('admin.')->group(function () {
Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/billing', fn () => 'Billing')->name('billing');
});
});
Nested groups merge middleware and append prefixes and route names.
Bad way vs better way
Bad way (repeating the same attributes on every route):
Route::get('/admin/users', fn () => 'Users')
->middleware(['auth'])
->name('admin.users');
Route::get('/admin/reports', fn () => 'Reports')
->middleware(['auth'])
->name('admin.reports');
Better way (single group contract):
Route::middleware(['auth'])
->prefix('admin')
->name('admin.')
->group(function () {
Route::get('/users', fn () => 'Users')->name('users');
Route::get('/reports', fn () => 'Reports')->name('reports');
});
Why better:
- one place to change shared concerns,
- fewer copy paste mistakes,
- cleaner route files as projects grow.
Verify grouped routes quickly
php artisan route:list --path=admin -v
Use -v to show middleware stacks so you can confirm group boundaries.
Grouping flow
flowchart LR
A[Group attributes] --> B[Apply prefix and middleware]
B --> C[Individual route names and actions]
C --> D[Consistent URL and access rules]
Next, you will go deeper into controllers so routes stay thin while request logic remains testable.
From Laravel best practices — Routing, Controllers & Request Flow