On Laravel, Full-Stack JavaScript, and Productive Frameworks

Is Laravel better than Remix? Is JavaScript better at the front-end? Which one should I use? Who's on first?

I’ve been a professional web developer for like 15 years: small websites with WordPress; Rails and Next.js for apps at big companies. I’ve designed a couple React meta-frameworks. I’ve used Laravel to build a side-project over the past five years.

Lots of people are talking right now about whether Laravel and Rails are better than full-stack JavaScript frameworks. Which is better for developers? Which has more hype? Which is more fun to use?

Here are some thoughts on everything from me, a random guy on the Internet!

I’m insanely productive with Laravel

Note: For the purposes of this section, you can pretty much swap in “Rails” for “Laravel,” because I think the ecosystems are similar and you can reach this level of productivity in both.

So, Barkpass is built with Laravel.

I created the project in 2019 as a Laravel API backend with a Next.js front-end. Separate repos, separate deploy pipelines.

The separation part sucked. I found myself having to duplicate everything, from authentication to authorization to form validation on both the front-end and the back-end. 

Solutions like Inertia now exist to solve this problem, but about a year into my Barkpass journey, I scrapped the front-end and started rebuilding it 100% in Laravel.

What does this look like five years later?

  • Laravel forall the things: Blade views and components, routing, controllers, authorization, authentication, queues, scheduled jobs
  • Livewire for modern interactivity: Form mutations without a browser refresh, SPA-like navigation between pages, full-page components, lazy loading, fancy data tables with filtering, search, pagination
  • Official Laravel packages: Nova for super-admin stuff, Horizon for queue management, Pulse for monitoring, Scout for full-text search, Jetstream for team management, Sanctum for auth scaffolding, Cashier for payments, Pennant for feature flags

I’m up to date on the latest versions of everything, so when Taylor and crew drop a new feature, I can start using it right away. This is super fun.

Like the heading says: I’m insanely productive in this stack. If I want to add a new feature, I can start by running:


php artisan make:model Foo -mft

Which gives me:

  • An Eloquent (Laravel) Model
  • A migration
  • A factory for tests
  • A unit test

Pretty dope.

If I feel like it, I can start doing some TDD to drive out API design and functionality. Or I can just drop in and start building visually.

Oh, time to add a new interactive piece of UI?


php artisan make:livewire foo --test

Which gives me a new Livewire component and a test for the component. If the component is pretty straightforward, I can route directly to it by defining a Route::get() to it! No need for a controller. Slap some authorization middleware on the route definition, and boom.

Instead of having to build out an explicit REST or GraphQL API, and then re-implementing all these things in a React or Vue front-end, it’s just all happening in PHP. Which feels lovely and liberating and powerful.

A couple things I need to call out here have made my experience in Laravel a delight, and would absolutely not be the case if they didn’t exist:

  • Blade components. Components are like your traditional server-side partial, but with special superpowers like composability. The ability to compose UI with HTML-like syntax has been an absolute game-changer: <x-foo :bar=”$thing” /> is actually way more appealing for me than @component(“foo”, [“bar” => $thing]) in that it makes me feel like I’m writing cool React code.
  • Caleb Porzio. Caleb created Livewire, which IMO gives Laravel’s Blade view compiler super powers. Modeled after Phoenix LiveView along with cousin Hotwire in the Rails space. The fact that I don’t have to write any JavaScript to be able to have interactive form submissions, SPA-like navigation, and more is incredible. But that’s not all: Caleb also created and maintains Alpine, which I like to think of as “sprinkles of JS,” along with screencasts and guides for handling the more tricky UI bits like dialogs, data tables, and dropdowns. Where Laravel gets me 90% of the way, Caleb is honestly the last 10% of the ecosystem which is the most crucial for getting things right.

I won’t spend any more time gushing about Laravel because I think there are problems, too, and there are merits to the full-stack JavaScript story!

Where Laravel falls short

Again, swap in Laravel here for other non-JavaScript full-stack solutions…

If you’re a Laravel developer reading this and looking for hard problems to solve: here’s your list.

Missing that “front-end” niceness

Laravel applications have been rated lower than alternatives when it comes to accessibility. I’m not an accessibility expert nor will I make any claims about it, but I will reframe this point into a more generic theme: 

The quality of front-end solutions does not match that of JavaScript frameworks… yet.

You guys, there’s a reason solutions like Angular, Vue, React, Svelte etc exist. It’s because building the really interactive stuff well requires a ton of work!

But Josh, we have a lot of cool new modern APIs and Vanilla JS works just fine and…!

Yeah I totally hear you, and we’re getting closer. But let’s step back and look at my own project, Barkpass, for a minute:

It’s built with Laravel and Livewire, so most of my interactivity happens as Livewire sends HTML payloads across the wire and makes server round trips. That can be slow — I’ve found this out the hard way 🙃

So what do you do when it’s slow? What do you do for users with slow or intermittent network connections? You start writing client JavaScript.

Alpine is the blessed solution in the Laravel/Livewire space, so you start adding x-data attributes into Livewire Blade components. A click handler here, an event listener there. It works! Ship it!

Unfortunately, this leaves a lot to be desired in some cases when it comes to not only accessibility, but also type safety, framework upgrades, and resiliency. 

The reason front-end frameworks like React Aria, Headless UI and Reach UI are so popular is that their creators have put an incredible amount of work into getting the details right. JavaScript frameworks like React Router, Remix, and Next.js also excel at data fetching, mutations, and optimistic UI.

I’m bummed to say that in Laravel land, you’re kind of on your own in this space. You really have to do the work yourself to learn about all the edge cases and acceptable solutions for accessibility. Caleb is doing work in this space with Headless Alpine UI components. I’m using these in Barkpass, and I’m eager to see these shipped to the world in a stable release!

But I’ve personally been burned by framework and package upgrades, like moving from Livewire v2 to Livewire v3, resulting in lots of uncaught client-side errors for my end users because… I didn’t catch them!

There’s no TypeScript runtime or linter to catch these issues. I guess I could run every single permutation of every action on every page through an E2E test and check for JS errors in the console…? But I know that we don’t have these issues over on the JS framework side.

Finally, while some of the stuff built into Livewire feels like building with composable React components, in reality… it’s not.

For example: you have to be super careful when composing Livewire components: in some cases, you need to define a unique key. In other cases, you don’t.

Livewire provides a “morph marker” feature which provides hints to the client-side DOM morph logic, but that breaks if you cache your views in production (which you probably do).

Sometimes, you can mark a property as “reactive” so it responds to updates, but in some cases, that falls short.

It’s the kind of stuff that wouldn’t happen in React land, but it’s a tradeoff I’m willing to accept for now!

Verbosity and lack of tooling

To combat the “Laravel is PHP and old school” and “why do I have so many files in this new project” arguments, you’ll see developers throw around code snippets written in Laravel Volt, or showcase file-based page routing with Laravel Folio. The Laravel team also stripped down the footprint of the starter template drastically in Laravel 11.

Volt and Folio are really great, and I think they showcase just how cool this ecosystem is… but we’re still not there.

For starters, important tooling is missing. I use VS Code, along with a ton of other people, and my IDE can’t figure out that I’m writing PHP code at the top block of a Laravel Volt component. Like, the LSP is completely confused. 

This is a solvable problem (again, take note, eager beavers), but it’s a deal breaker for the authoring experience.

Another bummer for me is that, while I love the composability of Blade components, I can’t write more than one component in a single file. In fact, it reminds me of one of my chief complaints about Vue, too.

It’s super easy to compose a complex component into sub-components in React! Just create a new function block below and reference it in the same file.

In Laravel, I have to create yet another file in my already-huge list of /components

Again: opportunity for Blade super nerds out there.

Finally: gosh it would be nice to have a proper formatting solution for Blade components. Prettier exists in this space for JavaScript projects, and Laravel Pint solves the problem for PHP classes, but not Blade components. I would pay someone American Dollars to not have to format a Blade file manually ever again.

Solvable problem.

Hosting costs money

In short: You gotta pay to host Laravel and other PHP apps. Even the hobby ones.

Compared to the JavaScript ecosystem which boasts free hosting for projects—Cloudflare Pages, Vercel, Netlify—this is a bummer.

I’ve tried and failed to build a li’l free hosting platform for PHP apps. Laravel Vapor is really neat, but it costs money and is geared toward business users. Also… AWS.

If I find dozens of hours of free time, I’m going to find a way to compile a real production Laravel app into WASM and deploy it to something like Cloudflare Pages. We’ll make it happen. Someday.

JavaScript and adjacent frameworks are exceptional at front-end things

I think one of the primary reasons people use JavaScript frameworks like Vue, Next.js, and Remix are that they’re really powerful to use on the front-end.

In most cases, the bar to entry is also really low. These days, you can pop into StackBlitz and start editing right in your browser.

For all the reasons mentioned above, JavaScript frameworks do the front-end really well. But you need a back-end, too, for routing and server rendering, which is why things like SSR in Next.js and Remix, and more recently—React Server Components—are born.

Shopify uses React and React Router heavily in its web app. Complex interactions on the UI call out to GraphQL requests which get handled by a Rails app.

The deployment story is also really great for these frameworks. In recent years, hobby projects are all free to deploy, especially if they receive little to no traffic.

If I change the props of my component in one part of my TypeScript app, the rest of my application will fail to build because I haven’t updated the props everywhere else. That’s really neat. I can also upgrade frameworks and have tests fail where I need to update things.

There are a plethora of resources for interactive components. Tailwind UI and shadcn/ui allow you to literally click to copy a really complex UI that would take hours to build otherwise.

So tl;dr—in my experience, it’s hard to beat the experience of building a really complex front-end web application in a modern JavaScript framework.

What about “full-stack JavaScript”?

Dang it if there isn’t just a huge opportunity here.

Unless you already have a backend service, it feels like you’re kind of on your own as a developer in the JavaScript ecosystem. Well, besides one of the bazillion third-party libraries or services which attempt to solve basic problems like authentication, authorization, form validation, queues, scheduled jobs, and more.

Why hasn’t anyone cracked this yet? 

Well, they have: Adonis exists, but nobody knows about it. 

I like a lot of things about Adonis, but I think their critical adoption mistake was to not hitch their wagon onto the popularity of existing front-end frameworks like React or Vue. Instead, they’ve built their own view rendering library. They’ve also copied almost every one of Laravel’s design patterns, a little too closely, IMO.

Friction

I also think there’s an enormous amount of friction when it comes to piecing everything together in the JavaScript ecosystem. 

I know I just complained about PHP hosting costing money (lol) but once you’re there, it’s super straightforward. If you’re running PHP, you’re running Laravel.

Also: if you’re running PHP, you probably also have access to a VPS or a real server somewhere, which means you have access to run additional processes for queue workers, scheduled jobs, databases, and cache.

Unless you’re deploying your JavaScript app to production in a VPS or Docker Kubernetes dealy, you’re gonna have to answer a lot of questions:

  • What runtime? Node.js or v8 or Deno or Bun? This will make a difference for the dependencies that you can use. You’ve probably been using Node.js as a development server, but those things will break in production when you deploy to Cloudflare Workers.
  • What about your bundler? Did you use CJS or ESM to write that package? Webpack, Vite, esbuild, SWC? How old are the versions? Do they play well together? Bundling and compiling is great for type safety and abstractions, but boy do you pay for it over time with version and tool creep.
  • How are you gonna do the full-stack things? Are you connecting to a 3p for a database? What ORM are you using, and what limitations does it have? Where is the database located, and is your deployment going to cause a bunch of latency based on the database location? Can you even communicate with your database over TCP in your production runtime?

Again, remember: PHP (or Ruby, etc) means you can deploy somewhere and have access to these things on your server.

Can existing JavaScript frameworks move to “insanely productive” full-stack status?

Yes, I think they can!

As I’m writing this, my former coworker Chance writes about Remix’s latest decision to double-down on the next version of React Router:

Now that it’s “just React Router” (not a real technical distinction btw) I think it might click more easily for some. Build an opinionated, battery-packed Laravelesque framework on top of a relatively thin layer that handles routing.

I’ve started on something like this with Superflare. It removes the friction listed above because Cloudflare does all of the things: database, queues, scheduled jobs, durable objects. But obviously it’s super opinionated to just one runtime (Cloudflare).

I think I can keep pushing with Superflare and potentially make it even more agnostic with drivers for Vercel, Netlify and other providers for things like databases and file storage.

However, one of the biggest problems about building a full-stack JavaScript framework is that you have to address routing. Routing is super key because it handles the request and response lifecycle. That also means handling things like cookies, which then relates to session management, which then relates to authentication.

That’s why I think Chance’s comment is so spot-on: you gotta lean into existing popular frameworks like React Router to do the hard parts like routing, and then add all the fun stuff around it.

Plus, there are exciting things on the horizon: React Server Components gets you even closer to making the front-end 🤝 the back-end more fluidly, and Cloudflare just announced Workers RPC for properly splitting up an application into multiple workers. This type of stuff could really be the bread and butter behind a new full-stack JavaScript framework.

If you want to build this with me, hit me up.

Can Laravel and existing full-stack frameworks move to “extremely good at front-end” status?

Yes, probably!

I think this involves creating lots of new tooling around developer experience for front-end things. Do the (good) things TypeScript offers for the ecosystem, but in the backend Laravel and Rails world.

There’s also a huge opportunity for someone to create the next “Headless UI” for server-rendered frameworks like Laravel and Rails. Like, do it super well. So I can upgrade my things and not have to rely on users or error tracking services to make sure I didn’t miss anything.

Take accessibility really seriously, and handle optimistic UI and fault tolerance with the care and dedication that the JavaScript frameworks have.

Oh, also: somebody please build a good hosting service that I can just php artisan deploy to and not have to worry about managing a server or paying somebody like $500/year to manage it for me.

Anyway, I love all of you. You are all great. Keep building cool things.

Believe