Modern Laravel & PHP Ecosystem: Rapid Full-Stack Web Development

19 min read
Software Engineering
Modern Laravel & PHP Ecosystem: Rapid Full-Stack Web Development

The honest starting claim

Laravel gets you from laravel new to a deployed, authenticated, database-backed SaaS skeleton faster than any other mainstream server-side framework except Rails, and even against Rails the gap is smaller than the marketing suggests. What Laravel actually wins on isn't code generation — it's that authentication, queues, caching, search, billing, and admin UI are first-party, maintained by the same team, and documented against each other. You don't spend day three of the project reconciling three different community packages' opinions about how sessions work. You spend day three writing the feature your client is paying for.

That's the real pitch. It comes with three costs nobody puts on the landing page: PHP's hosting story is more fragmented than it looks, Eloquent will let you write catastrophically slow queries without complaining, and the "just add Octane" performance advice is true in some deployments and nearly a rounding error in others. This piece covers the current stack — Laravel 13, PHP 8.3+, the post-Breeze auth story, and where the framework's official infrastructure (Forge, Vapor, Cloud) actually sits on cost.

What "current" means right now

Laravel 13 shipped in Q1 2026, and as of this writing it's the version you should be starting new projects on. Laravel's own support table draws a clean line:

Version PHP support Released Bug fixes until Security fixes until
10 8.1–8.3 Feb 14, 2023 Aug 6, 2024 Feb 4, 2025 (EOL)
11 8.2–8.4 Mar 12, 2024 Sept 3, 2025 Mar 12, 2026
12 8.2–8.5 Feb 24, 2025 Aug 13, 2026 Feb 24, 2027
13 8.3–8.5 Q1 2026 Q3 2027 Q1 2028

Source: Laravel's own release-notes support policy . If you're maintaining a Laravel 11 app right now, its bug-fix window already closed in September 2025 and it's living on security patches only until March 2026 — that's close enough that it belongs in your next sprint planning conversation, not your backlog.

Laravel 13 raised the PHP floor to 8.3, and a third-party compatibility write-up worth checking against your own composer.json notes that Laravel 13.3 and later pull in Symfony 8 components that, in practice, want PHP 8.4 even though 8.3 is technically still supported . If you're starting fresh, install PHP 8.4 and skip the argument. One industry write-up dates the Laravel 13 launch to March 17, 2026, announced live at Laracon EU — I can't verify the exact keynote date against a primary Laravel source, so treat the day-level precision as approximate, but the quarter is solid.

Scaffolding a project the way it actually works today

bash
composer create-project laravel/laravel my-saas
cd my-saas
php artisan install:api        # Sanctum, if you need first-party API auth
php artisan starter-kit:install react   # or vue, svelte, livewire

That last command is the part that changed. For years the answer to "how do I add login and registration" was laravel/breeze or laravel/jetstream. As of Laravel 12, that's no longer the recommended path. Laravel's own release notes for version 12 say plainly:

With the introduction of our new application starter kits, Laravel Breeze and Laravel Jetstream will no longer receive additional updates.

They still function — Breeze got a compatibility patch (v2.4.2) as recently as May 2026 — but new projects should use the React, Vue, Svelte, or Livewire starter kits, which bundle Fortify (auth backend) and Sanctum (API tokens) under the hood, plus a WorkOS AuthKit variant if you want social login, passkeys, and SSO without building it yourself . Jetstream still earns a look in exactly one situation: you need a built-in teams UI faster than you can build one yourself, and you're fine inheriting its opinions about how teams work .

The decision tree that actually matters for a client project in 2026 looks like this, and it's simpler than the six-package landscape suggests:

  • Building a new app, no legacy code: use the official starter kit matching your frontend choice. Don't reach for Breeze.
  • Need auth logic with zero UI (API-only backend, mobile-first, or a fully custom frontend): install Fortify directly and wire your own views to its routes.
  • Need to be an OAuth2 provider (third parties will authenticate against your app): Passport. This is genuinely rare — most apps consuming their own SPA or mobile client don't need it, and Passport's client/scope/grant machinery is overhead you don't want for that case .
  • Existing Breeze app, works fine, no active complaints: leave it. Migrating a working auth flow because a package is in maintenance mode is a self-inflicted wound.

Eloquent is fast to write and easy to make slow

Eloquent's expressiveness is the single biggest reason Laravel demos look impressive in twenty minutes. It's also the reason a huge share of "why is my Laravel app slow" tickets have the same root cause: the N+1 query problem, and it hides in code that reads as completely reasonable.

php
// Looks fine. Isn't.
$books = Book::all();

foreach ($books as $book) {
    echo $book->author->name;
}

With 20 books, this runs 21 queries — one to fetch the books, then one more every time $book->author triggers Eloquent's lazy loading . With 100 books, 101 queries. Nothing in the code above looks wrong on a diff review, which is exactly the problem — this is the textbook case where a database index or a caching layer can't save you, because the query pattern itself is the defect.

The fix is eager loading, and it's one line:

php
$books = Book::with('author')->get();

This runs two queries total regardless of how many books you have: one for the books, one WHERE author_id IN (...) for every author referenced . The same problem shows up nested — Post::with('comments.user') loads comments and each comment's author in one additional pass rather than cascading — and it shows up in packages you didn't write yourself, where a trait silently declares its own relationship that gets lazy-loaded on every model instantiation unless you override it .

Two things worth building into your workflow rather than your memory:

php
// In AppServiceProvider::boot(), outside production:
Model::preventLazyLoading(! $this->app->isProduction());

This throws an exception the moment code accesses an un-eager-loaded relationship, in dev and staging only . It's the difference between finding N+1 problems in a pull request review and finding them in a Sentry alert three weeks after a client's user base tripled. Pair it with assertQueryCount() in your feature tests:

php
public function test_book_index_avoids_n_plus_one(): void
{
    Book::factory()->count(20)->create();

    $this->get('/books')->assertQueryCount(2);
}

That test fails the day someone adds a relationship access inside the loop again, which is worth more than the eager-loading fix itself — it's the one line standing between "we fixed it" and "we fixed it until the next feature branch."

One more distinction that trips people up: with() decides eager loading at query time; load() adds it after the fact to a collection you already have; has() / whereHas() filter parent records by whether a relationship exists or matches a condition — they don't load data at all, and using them for loading is a common and expensive mistake .

Picking a frontend: Livewire, Inertia, and the argument nobody finishes

This is where most Laravel comparisons go soft, because both options are genuinely good and the "it depends" answer is technically correct and completely useless without the criteria. Here are the criteria.

Livewire keeps you in PHP. Every interaction — a keystroke with wire:model.live, a button click — sends a request to your Laravel server, which re-renders the component in PHP and diffs the result into the DOM . No API layer, no separate frontend build pipeline for the interactive parts, no context-switching between PHP and JavaScript mental models. Inertia keeps your Laravel routing and controllers but renders the actual page with React, Vue, or Svelte, without you having to build and version a REST or GraphQL API to feed it .

The trade-off that actually decides this for most teams isn't philosophical, it's physical: every Livewire interaction is a network round trip. One comparison piece measured 350ms of typing latency on a 4G connection with Livewire against effectively zero with Inertia, because Inertia's state lives in the browser . That's not a Livewire bug — it's the architecture working as designed — but it means Livewire's cost scales with how chatty your UI is and how bad your users' connections are. A CRUD-heavy internal dashboard on office wifi won't notice. A public-facing form with live validation on mobile data will.

Both shipped major versions in 2026: Livewire 4 in January, Inertia 3 in March . Livewire 4's most consequential change for existing apps is quiet — wire:model on a container element used to catch events bubbling up from children; now it only listens to events from the element itself, which breaks silently rather than throwing an error, and shows up as "the modal stopped updating" two sprints after the upgrade . If you're upgrading a Livewire 3 app, grep for wire:model on anything that isn't a leaf input before you ship.

For adoption, the State of Laravel 2025 community survey put Livewire usage at 62% and Inertia at 48% among respondents (the categories overlap, since teams use both for different parts of the same app) — treat that figure as directional rather than authoritative, since it's a self-selected community survey rather than a JetBrains- or Stack Overflow-scale sample, but it matches what the ecosystem's package downloads and job postings suggest.

Pick Livewire when: your team is PHP-first, the UI is dashboards and CRUD, and you want to ship without maintaining a second toolchain. Pick Inertia when: you need genuinely complex client-side state (drag-and-drop boards, live charts, anything with meaningful client-side interaction logic), your team already knows React or Vue, or you might need a public API for the same data later and want the separation baked in from day one .

The admin panel isn't a build task anymore

If part of "client-ready" means a back office where non-technical staff manage records, don't build it. Filament is a free, open-source panel builder on top of Livewire, Alpine.js, and Tailwind that generates resource CRUD screens, tables with sorting/filtering/search, and dashboard widgets from PHP class definitions :

php
public static function table(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('title')->searchable()->sortable(),
            BadgeColumn::make('status')
                ->colors(),
            ToggleColumn::make('is_featured'),
        ])
        ->filters([
            SelectFilter::make('category'),
        ])
        ->actions()
        ->bulkActions();
}

That's a searchable, sortable, filterable admin table with inline editing, and it took less code than the migration for the table it manages. Version 4 unified the forms, tables, and widgets into one schema package, which mostly matters if you're extending Filament's internals rather than just consuming it . A word of caution for anyone reading a v4 tutorial today: Filament 5's beta requires Livewire 4 and existing v4 code largely stops working under it until you migrate, so pin your Filament version deliberately in composer.json rather than letting it float on a caret constraint during an active client engagement .

This is the single biggest lever on "fastest path to client-ready" that the framework-versus-framework debates skip entirely. Rails has good scaffolding for generating a resource. Laravel has a package that generates the entire internal-facing application around your resources, styled, with permissions, in an afternoon.

Octane, FrankenPHP, and the benchmark that disagrees with the vendor

Laravel Octane keeps your application booted in memory across requests instead of rebuilding the service container, routes, and config on every single one — the standard PHP-FPM model . Laravel's official pitch, echoed by most third-party benchmarks, is substantial: 2.5–3x higher throughput and meaningfully lower latency, at the cost of higher baseline memory use and the need to audit your code for state that leaks between requests via static properties or singletons .

FrankenPHP, a Go-based server with native worker mode, has become Octane's default recommendation for new deployments — it ships as a single binary with TLS handled via Caddy, needs no separate process manager, and it's what Laravel's own team highlighted when they added it as an Octane driver .

Here's the disagreement, and it's the kind of thing that never survives into a vendor blog post: an independent write-up measured FrankenPHP in classic (non-worker) mode within about 1% of plain PHP-FPM — 7,023 requests/second against 6,934 in one test — and concluded that the entire performance gain from Octane comes from skipping the bootstrap phase on every request, which only matters when bootstrap is a meaningful fraction of your request's total time . For an app with three or four Eloquent queries and a template render, bootstrap often isn't the bottleneck, and Octane buys you very little. For an app with a heavy service container, dozens of registered providers, or slow autoloading, the win is real.

Neither claim is wrong. They're measuring different applications. The practical conclusion: don't add Octane to a project because a benchmark says 2.5x — profile your own app's bootstrap time against its total request time first (php artisan octane:status and a simple microtime() bracket around Application::handle() will tell you in ten minutes), and only reach for Octane if bootstrap is actually a meaningful share of the number. If your bottleneck is three unindexed Eloquent queries, Octane will make you fail faster, not fix anything.

Separately, benchmarks that compare Swoole, RoadRunner, and FrankenPHP against each other under real concurrency and mixed I/O/DB workloads (not just a static welcome-page stress test) consistently find that the "best" driver depends on workload shape more than any of the three being categorically faster — Swoole's coroutines help most when your request does a lot of outbound HTTP calls in parallel; FrankenPHP wins on deployment simplicity; RoadRunner fits teams already running Go infrastructure elsewhere .

Deployment: Forge, Vapor, and the newer, cheaper option most comparisons haven't caught up to

Three first-party paths exist, and their pricing models are different enough that "which is cheapest" depends entirely on your traffic shape.

Forge manages servers you still pay for separately — it doesn't host anything itself. It configures Nginx, SSL, queue workers, the scheduler, and deployments on a VPS from DigitalOcean, Hetzner, AWS, or similar. As of 2026 the tiers are Hobby at 12/month,Growthat12/month, Growth at 19/month, and Business at 39/month,billedperaccount,notperserver,withunlimitedsitesoneverytier.AddtheserverbillontopacapableHetznerboxcanrun39/month, billed per account, not per server, with unlimited sites on every tier . Add the server bill on top — a capable Hetzner box can run 6–$20/month for a small app, considerably more on AWS at the same specs . Forge is the right call if someone on your team is comfortable with Linux and you want full control over MySQL/Postgres/Redis versions and tuning.

Vapor runs your app serverlessly on AWS Lambda. It costs $39/month plus AWS usage (S3, SQS, RDS or Aurora, ACM, data transfer), and its ephemeral filesystem means every file upload and generated asset has to go to S3 — that's a real refactor if your app currently writes to local disk . Cold starts add 800–1,200ms to roughly 0.3% of requests . The one thing Vapor can't do at all: Laravel Reverb and any persistent WebSocket connection, because Lambda functions don't stay alive between requests. Teams running Vapor for the app commonly run a small Forge server just for Reverb and heavy queue workers — a hybrid that's a legitimate production pattern, not a compromise .

Laravel Cloud is the newer option, and it changes the calculus for anything with genuinely spiky or low traffic. The Starter plan has no fixed monthly minimum beyond 5,whichcomesbackasusagecredit,andapplications,databases,andcacheallscaletozerowhenidleyoupaynothingforcomputeduringdeadtime.LaravelsownpricingupdateinAugust2026movedtheGrowthtierto5, which comes back as usage credit, and applications, databases, and cache all scale to zero when idle — you pay nothing for compute during dead time . Laravel's own pricing update in August 2026 moved the Growth tier to 20/month with added autoscaling, preview environments per pull request, and MySQL database support baked in, on top of the serverless Postgres option that was already there . For a side project, an internal tool used a few times a week, or the six weeks between "MVP live" and "MVP has real traffic," Cloud's pay-for-what-you-use model is very hard to beat on cost precisely because Forge and Vapor both have a fixed floor even at zero traffic.

The decision that actually matters: if your workload needs persistent processes — WebSockets, long-running queue workers, anything that can't tolerate a cold start — start with Forge. If your traffic is unpredictable and you'd rather not think about infrastructure at all, start with Cloud and only move to Forge if cost or fine-grained control become the limiting factor later.

The comparison that actually decides "fastest path to client-ready"

Every Laravel-versus-X article eventually asks this, so it's worth answering directly rather than dodging into a feature table.

Against Ruby on Rails, the honest answer is that Rails is still faster for the first hour. Its convention-over-configuration approach and rails generate scaffold produce a working model, controller, view, and test suite from one command, and that head start is real . Where Laravel closes the gap and then passes Rails is week two through week eight of a real client project: first-party billing (Cashier), queue monitoring (Horizon), full-text search (Scout), and — critically — Filament's admin panel generation aren't things you assemble from gems of inconsistent quality and vintage. You're also hiring into a larger, cheaper talent pool; PHP developers are more numerous and Laravel specifically dominates PHP framework usage, which matters the moment you need to bring on a second or third engineer .

Against Django, the comparison is really about what kind of application you're building, not which framework is "better." Django's batteries-included philosophy front-loads more setup than Laravel for a generic CRUD app, but it pays off for anything data-heavy, analytics-oriented, or adjacent to Python's ML ecosystem — a use case Laravel doesn't compete in and shouldn't try to . For a B2B SaaS dashboard with billing and role-based access, Laravel's ecosystem is more directly aimed at exactly that shape of problem .

Against Symfony, this is closer to a real fight, because both are PHP and both are mature. Symfony's component-based architecture gives more architectural control and scales better into genuinely complex, multi-role, long-workflow enterprise systems — Drupal and Magento are built on Symfony components for a reason . But for the specific question this article is answering — the fastest path from zero to a client-ready SaaS prototype — Laravel's cohesion wins. According to the JetBrains State of PHP 2025 survey of 1,720 developers, 64% use Laravel as their primary framework against 23% for Symfony , and one industry comparison puts Laravel's installed base above 960,000 sites — though I'd treat that specific figure as approximate since it comes from a single secondary source rather than a JetBrains or W3Techs count. The 2025 Stack Overflow Developer Survey found Symfony slightly more "admired" by the developers who use it, despite Laravel's larger footprint — which is a genuinely interesting split: more people choose Laravel, but a slightly higher share of Symfony's smaller user base would choose it again. That's worth knowing if your team is picking based on long-term developer satisfaction rather than raw speed to a working prototype.

My position, stated plainly: for the brief in this article's summary — rapid SaaS prototyping, client-ready execution speed — Laravel is the right default. Not because it's technically superior in every dimension, but because the parts of a real project that eat unplanned time (auth, admin UI, background jobs, deployment, billing) are solved once, by one team, and documented against each other. Symfony is the better choice the moment your project's complexity is genuinely enterprise-shaped from day one — many roles, long approval workflows, integration with several other backend systems. Rails is still worth it if your team already has deep Rails expertise and no PHP background; converting a team's existing muscle memory costs more than any framework-level speed difference.

What PHP 8.4 actually gives Laravel developers, concretely

Laravel 13's PHP 8.3 floor (8.4 recommended) matters beyond compliance — PHP 8.4 shipped property hooks and asymmetric visibility, and both change how you write Eloquent-adjacent value objects and DTOs, not just plain PHP classes .

Property hooks replace the getter/setter boilerplate directly in the property declaration:

php
class Money
{
    public function __construct(
        private int $cents,
    ) {}

    public string $formatted {
        get => number_format($this->cents / 100, 2);
    }
}

No separate getFormatted() method, no docblock trying to keep IDEs honest about a computed value . Asymmetric visibility solves a narrower but common pain point — a property that should be publicly readable but only writable from inside the class:

php
class Order
{
    public private(set) string $status = 'pending';

    public function markShipped(): void
    {
        $this->status = 'shipped'; // fine, inside the class
    }
}

// $order->status = 'shipped'; // fatal error from outside

Before 8.4, this required a private property plus a public getter method just to enforce read-only-from-outside semantics . Neither feature is Laravel-specific, but if you're writing value objects for Eloquent casts or DTOs for API responses — which you should be doing instead of passing raw arrays through your application — these two features remove a genuine category of boilerplate you were writing in every Laravel project built on 8.1 or 8.2.

What to actually do this week

If you're starting a new Laravel project: use PHP 8.4, install via a starter kit rather than Breeze, and decide Livewire versus Inertia based on how chatty your interface needs to be on a bad connection — not based on which one your last project used. If you're running an existing Laravel 11 app, check where you sit against that support table above; security-only status ends in March 2026, and "we'll upgrade eventually" stops being a plan the day a CVE lands against a package you can't patch on your current major version.

The framework's genuine advantage isn't raw execution speed of the runtime — Octane helps some apps and does almost nothing for others, and pretending otherwise wastes an afternoon profiling the wrong layer. It's that the boring 80% of every client project — who's logged in, what they're allowed to see, who gets billed, what the internal team uses to manage records — is already built, and built by people who have to keep it working for a living. Spend the time you save there on the 20% that's actually specific to the client sitting across from you.

STAY CONNECTED WITH THE EXPAT COMMUNITY

Subscribe to get expat tips, local insights, and connect with professionals around the world.