Update, October 2026: I first wrote this in 2015. I updated the code for current Laravel and added a section on how I'd set it up today.

A while back, I worked on a project with a designer/front-end developer hybrid who didn't have much experience with web frameworks or back-end-developer-ish stuff in general. We wanted them to commit design changes straight to our main Git repository, instead of us pulling templates in from a separate FTP site or design repo.

Ideally, they could flesh out new app and marketing screens without anyone having to sync HTML, CSS and JavaScript by hand. Thankfully, we came up with a catch-all route that lets anyone add a page by adding a view, with no controller and no back-end code.

The catch-all route

The route takes the URL path and returns the view at the same path. You probably know the view() helper takes dot syntax that maps to the file system, so view('people.david') returns resources/views/people/david.blade.php. What makes this work is that it also takes slashes, like view('people/david'), so the URL path can go straight into the helper:

php
Route::middleware('auth')->group(function () {
    Route::get('{view}', function (string $view) {
        abort_unless(view()->exists($view), 404);

        return view($view);
    })->where('view', '.*');
});

We put this at the very end of routes/web.php, so it only catches what the other routes don't. The auth middleware keeps it behind a login. Laravel hands everything after the domain to the closure as a string, so a request for /contact/thank-you looks for resources/views/contact/thank-you.blade.php. If that view doesn't exist, the visitor gets the normal 404 page.

How I'd set it up today

Laravel 5.5 added Route::fallback(), which only runs when no other route matches. It's a better spot for this than a {view} route at the bottom of the file, because a route you add later can't get swallowed by it.

I'd also point it at one folder, like resources/views/pages. The version above will render any view by URL, including layouts, partials and email templates. A request for /layouts/app would render your layout with none of the data it expects.

php
Route::fallback(function () {
    $view = 'pages/'.request()->path();

    abort_unless(view()->exists($view), 404);

    return view($view);
})->middleware('auth');

And if it's only a handful of static pages, Route::view() is simpler still:

php
Route::view('/contact/thank-you', 'pages.contact.thank-you');