laravel/folio
Laravel Folio is a page-based router for Laravel that maps routes from your filesystem to simplify routing. Define pages as files, reduce route boilerplate, and build apps faster with a clean, convention-driven approach.
Installation:
composer require laravel/folio
php artisan folio:install
This generates the default resources/views/pages directory and config file.
First Route:
Create a Blade file at resources/views/pages/about.blade.php.
Access it via /about—no manual route definition needed.
Verify:
Run php artisan folio:list to see auto-generated routes.
config/folio.php (adjust path, domain, or middleware).folio:install (setup)folio:list (view routes)folio:make (generate a new page file with namespace support).Static Marketing Site:
resources/views/pages/ (e.g., home.blade.php, contact.blade.php)./home, /contact without routes/web.php changes.php artisan folio:list to debug route conflicts or missing pages.File-Based Routing:
resources/views/pages/{slug}.blade.php → /{slug}.resources/views/
└── pages/
├── about.blade.php # /about
├── blog/
│ ├── post-1.blade.php # /blog/post-1
│ └── index.blade.php # /blog (implicit index)
path in config/folio.php to use resources/views/content/.Model Binding:
resources/views/pages/posts/{post}.blade.php).$post (injected automatically).// routes/web.php (optional, for custom logic)
Folio::bind('posts', Post::class);
Middleware:
config/folio.php:
'middleware' => ['web', 'auth'],
@middleware(['guest'])
Dynamic Domains:
resources/views/
└── pages/
├── admin/
│ └── dashboard.blade.php # admin.example.com/dashboard
└── blog/
└── post.blade.php # blog.example.com/post
config/folio.php:
'domains' => [
'admin' => 'admin.example.com',
'blog' => 'blog.example.com',
],
Nested Routes:
resources/views/pages/
└── products/
├── electronics/
│ └── laptops.blade.php # /products/electronics/laptops
└── index.blade.php # /products
routes/web.php:
Route::get('/legacy', function () { ... });
Folio::mount(); // Mount Folio after legacy routes.
routes/api.php.Folio::fake() in tests to mock routes:
use Laravel\Folio\Facades\Folio;
public function test_page_routing()
{
Folio::fake();
$response = $this->get('/about');
$response->assertViewIs('pages.about');
}
php artisan route:cache to cache Folio routes (works alongside Laravel’s route caching).Route Overlaps:
index.blade.php in nested directories may conflict with parent routes.@route(['name' => 'products.index'])
php artisan folio:list --verbose to inspect route conflicts.Case Sensitivity:
About.blade.php vs. about.blade.php).Middleware Conflicts:
config/folio.php may override app-wide middleware.unless or only in app/Http/Kernel.php:
protected $middleware = [
// ...
FolioMiddleware::class => ['unless' => ['folio']],
];
Model Binding Quirks:
UUID) may not bind correctly.FolioServiceProvider:
Folio::bind('posts', Post::class, 'uuid');
Wildcard Directories:
my-page) may break routing.php artisan folio:list --verbose.ViewMatched events to debug unresolved views:
event(new ViewMatched($view, $route));
config/folio.php:
'debug' => env('FOLIO_DEBUG', false),
Multiple Mount Paths:
resources/views/pages and resources/views/admin).'paths' => [
'pages' => 'resources/views/pages',
'admin' => 'resources/views/admin',
],
admin/dashboard vs. pages/admin/dashboard).Custom View Resolver:
FolioViewFinder:
Folio::extend(function ($app) {
$app->singleton('view.finder', function () {
return new CustomFolioViewFinder;
});
});
Terminable Middleware:
Folio::terminable(function ($request) {
if ($request->ip() === '123.123.123.123') {
abort(403);
}
});
Custom Route Naming:
pages.{slug}):
Folio::name(function ($view) {
return 'custom.'.str_replace('/', '.', $view);
});
Pipeline Extensions:
$app->extend('folio.pipeline', function ($pipeline) {
$pipeline->append(\App\Http\Middleware\CustomMiddleware::class);
return $pipeline;
});
Dynamic Route Generation:
$routes = Folio::getRoutes();
foreach ($routes as $route) {
// Process routes (e.g., log, cache, or modify)
}
Event Listeners:
FolioRegistered (after routes are registered) or FolioBooted (after booting):
Folio::listen('FolioRegistered', function () {
// Modify routes dynamically
});
a/b/c/d.blade.php) can slow down route resolution.php artisan route:cache in production to improve performance.folio:make) are lazy-loaded by default (v1.1.7+), reducing startup overhead.How can I help you explore Laravel packages today?