Laravel · Laravel

Laravel Folio File-Based Routing

How Laravel Folio's file-based routing convention maps page components directly to URLs, what it's actually built for, and where it fits alongside standard route files.

John Kihiu12 min read

Folio is Laravel's answer to file-based routing: instead of declaring every route explicitly in routes/web.php, you place a PHP file (usually a Blade/Volt view) inside a designated pages directory, and the path to that file becomes the URL. It's the same convention Next.js and Nuxt popularized for JavaScript frameworks, applied to Laravel's own routing layer — genuinely useful for content-heavy or page-dominant sites, and unnecessary ceremony for an API or an app that's mostly REST controllers.

How the file-to-URL mapping actually works

After installing Folio (composer require laravel/folio and php artisan folio:install), pages live under resources/views/pages by default. pages/index.blade.php becomes /, pages/contact.blade.php becomes /contact, and nested directories become nested paths: pages/settings/profile.blade.php becomes /settings/profile. There's no separate route file to keep in sync with the view — the file's location on disk is the route.

Bash · directory structure to URL
resources/views/pages/index.blade.php          -> /
resources/views/pages/contact.blade.php        -> /contact
resources/views/pages/settings/profile.blade.php -> /settings/profile
resources/views/pages/posts/[post].blade.php   -> /posts/{post}

Dynamic segments and route model binding

Square-bracket filenames define dynamic route segments, exactly like Next.js's convention. [post].blade.php captures a post parameter, and if that parameter's name matches an Eloquent model type-hinted in the page's <?php ... ?> block or a name() call, Folio performs route model binding automatically — resolving the model instance from the route segment the same way a controller method would.

PHP · resources/views/pages/posts/[post].blade.php
<?php

use App\Models\Post;
use function Laravel\Folio\{middleware, name};

middleware(['auth']);
name('posts.show');

?>

<x-layout>
    <h1>{{ $post->title }}</h1>
    <div>{{ $post->body }}</div>
</x-layout>
Folio pages get named routes and middleware without a routes file

The middleware() and name() helpers called inside the page file itself replace what would otherwise be route-file configuration — so route('posts.show', $post) works from anywhere in the app exactly as it would for a normally declared named route.

Where Folio sits alongside Inertia and Livewire

Folio is a routing convention, not a rendering approach — it works fine with plain Blade views, and it pairs particularly well with Livewire/Volt's single-file component syntax, since a Folio page and a Volt component can be the same file. It's less commonly paired with Inertia, since Inertia's own routing typically flows through standard Laravel routes that dispatch to page components on the frontend framework's side; combining both means picking which one owns route definition for a given section of the app, rather than using both for the same URLs.

When file-based routing is worth adopting, and when it's not

Folio earns its keep on marketing sites, documentation, dashboards with a large number of mostly-static pages, or any app where the page-to-URL mapping is naturally 1:1 and route files were becoming a long list of nearly identical Route::view() calls. It's a poor fit for an API, or for routes that need heavy custom logic before rendering (complex authorization chains, multi-step wizards) where an explicit controller with clear method boundaries is easier to follow than logic embedded in a page file's PHP block. Standard routes and Folio pages can coexist in the same app — it's not all-or-nothing.

Wrapping up

Folio trades explicit route declarations for a directory convention: the file's path is the URL, dynamic segments use bracket syntax with automatic model binding, and middleware/naming are declared inline in the page rather than in a separate routes file. It's a genuine win for page-heavy, mostly-static sections of an app, and not a replacement for controllers on routes that need real request-handling logic.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.