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/users named admin.users
  • /admin/reports named admin.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