← All posts
Laravel · PHP

Exploring Laravel 11: What's New for Web Developers

THE SLIMMER SKELETONapp/Controllers, Models, Providersbootstrap/app.phprouting, middleware, exceptionsroutes/web.phpapi added on demandconfig/only what you publish

Originally published on Medium.

Laravel 11 is all about making things simpler and more efficient. It is a cleaner, less cluttered space to write code in.

Before and now: a quick look

Previously, a new Laravel project included several directories and files that many projects never touched. With Laravel 11 the structure is more focused:

  • Old structure — folders like app/Console, app/Exceptions and app/Http/Middleware, all necessary but overwhelming for a simple application.
  • New structure
    • app/ now mainly holds Http/Controllers/, Models/ and Providers/.
    • bootstrap/ is simplified to app.php and providers.php.
    • routes/ drops channels.php, console.php and api.php.

Optional, not forced

Existing projects can keep their current structure. The new layout is the default for new projects only, and you can adopt as much or as little of it as you want.

Where routes, middleware and exceptions went

Routes, middleware and exception handling, which each had their own dedicated file, are now configured in one place:

// bootstrap/app.php

use Illuminate\Foundation\Application;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(web: __DIR__.'/../routes/web.php')
    ->withMiddleware(/* middleware configuration here */)
    ->withExceptions(/* exception handling here */)
    ->create();

Configuration files, trimmed

Some config files were removed because they were rarely touched — config/broadcasting.php and config/cors.php among them. You can bring back only the ones you actually need to change:

php artisan config:publish --tag=auth

API scaffolding on demand

Not every project serves an API, so Laravel 11 no longer ships those routes and configuration by default. When you do need them:

php artisan install:api

Broadcasting becomes installable

Broadcasting follows the same rule. Add it only when your app really does push real-time updates:

php artisan install:broadcast

New defaults: Pest and SQLite

Laravel 11 makes Pest the default testing framework. Its syntax is short enough that writing a test stops feeling like a chore:

it('confirms the user can view the homepage', function () {
    $response = get('/');
    $response->assertStatus(200);
});

SQLite becomes the default database for local development, which removes most of the setup between cloning a project and running it:

DB_CONNECTION=sqlite
DB_DATABASE=/path/to/database.sqlite

New Artisan commands

Three generators worth adding to muscle memory:

php artisan make:enum Status
php artisan make:class Services/PaymentService
php artisan make:interface Contracts/PaymentInterface

A health check route

Laravel 11 adds a dedicated /up route so a monitor, a load balancer or a Kubernetes readiness probe can ask whether the application is alive:

use Illuminate\Support\Facades\Route;

Route::get('/up', function () {
    return 'Application is up and running!';
});

It is worth extending it to cover the things that actually break, rather than only proving that PHP responded:

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Route;

Route::get('/up', function () {
    try {
        DB::connection()->getPdo();

        return 'Application is up and running, database connection is ok!';
    } catch (\Exception $e) {
        return response('Failed to connect to the database', 500);
    }
});

The Dumpable trait

The Dumpable trait can be added to any class to give it dump() and dumpIf() without stopping execution — useful when halting the request would break the thing you are trying to observe:

use Illuminate\Support\Traits\Dumpable;

class UserProfile
{
    use Dumpable;

    protected $name;
    protected $email;

    public function __construct($name, $email)
    {
        $this->name = $name;
        $this->email = $email;
    }
}
$userProfile = new UserProfile('John Doe', '[email protected]');
$userProfile->dump();
$userProfile->dumpIf($condition);

Limiting eager loads, natively

Before Laravel 11, loading "the latest ten posts for each user" meant a workaround or an extra package. Now the limit goes straight into the eager load:

$users = User::with(['posts' => function ($query) {
    $query->latest()->limit(10);
}])->get();

The same applies when you need several relationships at once — a dashboard showing each user's five most recent posts, comments and likes:

$users = User::with([
    'posts' => fn ($query) => $query->latest()->limit(5),
    'comments' => fn ($query) => $query->latest()->limit(5),
    'likes' => fn ($query) => $query->latest()->limit(5),
])->get();

Casts as a method

Model casts move from a $casts property to a casts() method, which means the cast list can now contain logic:

class User extends Model
{
    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'is_admin' => 'boolean',
        ];
    }
}

Custom cast classes can be referenced by name, which keeps model definitions clean and the conversion logic in one reusable place:

protected function casts(): array
{
    return [
        'price' => CurrencyCast::class,
    ];
}
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class CurrencyCast implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes)
    {
        return convertCurrency($value, session('currency', 'USD'));
    }

    public function set($model, string $key, $value, array $attributes)
    {
        // Optionally implement the reverse conversion for saving.
    }
}

Wrapping up

Laravel 11 is mostly about removing things you were never using and making the rest easier to find. Nothing here forces a rewrite of an existing project, but a new one starts from a much smaller surface.

Worth reading next: the Laravel 11 overview and the directory structure notes.

Manish Kumar

I've been working with PHP for over 11 years and focusing on Laravel for the last 6 years. During this time, I've taken part in big projects and gotten to know tools like Laravel Livewire really well. Besides working on projects, I enjoy writing about Laravel on LinkedIn and Medium to share what I've learned. I'm always looking to learn…

Keep reading

ALL POSTS →
Start a project

Need this built properly?

We build Laravel and React products for businesses that cannot afford downtime. Send the workflow and the deadline, and you will get a practical next step.