In this Laravel 12 tutorial titled “laravel 12 RouteServiceProvider configuration tutorial “, you’ll learn how to configure custom route Step by Step with example.
In Laravel 12, the routing system has undergone a significant transformation. The traditional RouteServiceProvider.php file that existed in earlier versions has been completely removed, replaced with a modern, more intuitive configuration system in bootstrap/app.php. This change simplifies route management while providing more control and flexibility for developers.
If you’re upgrading from Laravel 10 or 11, or if you’re new to Laravel 12, this comprehensive guide will walk you through understanding how routes are now configured, customized, and organized in your Laravel 12 applications.
Table of Contents
Prerequisites
Before diving into this tutorial, ensure you have:
- A fresh Laravel 12 installation or an existing Laravel 12 project
- Basic understanding of Laravel routing concepts (Route::get, Route::post, etc.)
What Happened to RouteServiceProvider?
In Laravel 10 and earlier versions, route configuration was handled through app/Providers/RouteServiceProvider.php.
public function boot()
{
$this->routes(function () {
Route::middleware('web')
->prefix('admin')
->group(base_path('routes/admin.php'));
});
}
While this approach worked, it added an extra layer of abstraction. Developers had to understand service providers, the boot method, and the routes closure—multiple concepts bundled together.
The Laravel 12 Approach
Laravel 12 eliminates this complexity by moving route configuration directly into bootstrap/app.php, where all application setup happens. This is part of Laravel’s broader simplification initiative that began in Laravel 11.
Default configuration in Laravel 12:
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
//
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
Benefits of this approach:
- All application configuration is in one place
- Cleaner, more readable code
- Faster to understand application structure
- Less boilerplate code
- Direct access to routing configuration without service provider abstraction
Step 1: Create Your Custom Route File
Let’s create a custom admin route file for managing admin-specific routes.
Create routes/admin.php:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\LocationController;
Route::get('/show', [LocationController::class, 'show']);
Route::get('/test', [LocationController::class, 'test']);
Read Also : Laravel 12 Restrict/Block User Access from IP Address
Step 2: Register the Route File in bootstrap/app.php
Use the then callback in the withRouting() method:
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
Route::middleware('web')
->prefix('admin')
->name('admin.')
->group(base_path('routes/admin.php'));
},
)
->withMiddleware(function (Middleware $middleware) {
//
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
What this does:
- middleware(‘web’): Applies web middleware (session, CSRF protection) to admin routes
- prefix(‘admin’): All routes get /admin prefix (e.g., /admin/show)
- name(‘admin.’): All route names get admin. prefix (e.g., admin.show)
- group(): Loads the admin route file
Now your admin routes are accessible at:
- http://127.0.0.1:8000/admin/show
- http://127.0.0.1:8000/admin/test
Step 3: Verify Routes Are Loaded
Run the route list command:
php artisan route:list
You should see your admin routes listed with the /admin prefix:

Multiple Custom Route Files
For a larger application, you might want to organize routes by feature or role:
routes/
├── web.php
├── console.php
├── admin.php
├── vendor.php
Register in bootstrap/app.php:
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
// Admin routes
Route::middleware('web')
->prefix('admin')
->name('admin.')
->group(base_path('routes/admin.php'));
// Vendor routes
Route::middleware('web')
->prefix('vendor')
->name('vendor.')
->group(base_path('routes/vendor.php'));
},
)
Read Also : Laravel 12 Database Seeder One to Many Relationship Example
Troubleshooting Common Issues
Issue 1: Custom Routes Not Loading
Problem: You’ve created a custom route file but routes aren’t showing in route:list.
Solution: Ensure you’ve registered the route file in bootstrap/app.php with the then callback:
->withRouting(
// ... default configuration
then: function () {
Route::middleware('web')
->prefix('custom')
->group(base_path('routes/custom.php'));
},
)
Then clear the route cache:
php artisan route:clear
Issue 2: Middleware Not Applying
Problem: Routes aren’t respecting the middleware you specified.
Solution: Check that middleware is registered and applied in the correct order:
// Register middleware alias first
->withMiddleware(function (Middleware $middleware) {
$middleware->alias(['admin' => IsAdmin::class]);
})
// Then use in routes
then: function () {
Route::middleware('admin') // Make sure this matches the registered alias
->group(base_path('routes/admin.php'));
}
Conclusion
Laravel 12’s approach to route configuration through bootstrap/app.php represents a significant improvement in code organization and developer experience. By eliminating the RouteServiceProvider class, Laravel has made it easier to understand the application bootstrap process at a glance.
key takeaways:
- All route configuration now happens in bootstrap/app.php
- Use the withRouting() method to configure web, API, and console routes
- The then callback allows you to register additional custom route files
- The using callback provides complete routing control (use with caution)
- Organize routes into separate files for better maintainability
- Always cache routes in production for optimal performance
- As you build more complex Laravel 12 applications, remember that keeping your routes organized and clearly named will make your codebase more maintainable and easier to understand for both you and your team members.
Q1: Can I still use service providers for routing?
In Laravel 12, routing is primarily handled in bootstrap/app.php. However, you can register additional routes in service providers if needed, but it’s not the recommended approach for main route configuration.
Q2: What’s the difference between then and using callbacks?
The then callback registers additional routes alongside the default web and API routes. The using callback takes complete control—you must manually register all routes (but the health check is still available).
Q3: How do I migrate from RouteServiceProvider to the new system?
Move your route registration logic from RouteServiceProvider::boot() method to the then callback in bootstrap/app.php. Replace $this->routes(function() { … }) with the then callback.
Q4: Can I group routes by domain?
Yes! Use the domain() method in your route definitions:
Route::domain(‘api.myapp.com’)
->prefix(‘v1’)
->middleware(‘api’)
->group(function () {
Route::get(‘/posts’, [PostController::class, ‘index’]);
});
Q5: How do I test route registration?
Use the artisan commands:
# List all routes
php artisan route:list
# List only specific routes
php artisan route:list –path=admin