Recently, the Laravel team made it easy to set up a React stack with just one command, making Laravel a viable full-stack React framework.
Madness, you say? Well, what makes Next.js and other similar React frameworks so great?
They’re great because they allow for better server-side rendering, routing, authentication, state management, and session management, to say the least.
In this article, we’ll show you why Laravel Breeze’s Inertia-React stack is so much better and easier to use than Next or Gatsby. Even though this is about Laravel’s suitability as a React framework, many of these points can be understood in a Vue or Svelte context, too!
Laravel Breeze is one of the starter kits that was introduced with Laravel 8 in fall 2020 — the other one is Jetstream. Both Laravel Breeze and Jetstream come with baked-in authentication, as well as the routes, controllers, and views that you’ll need to quickly set up a large application. Breeze also comes with frontend scaffolding. Both are styled with Tailwind CSS.
Though you can use normal Blade templates with Laravel Breeze, you can also use the Inertia.js stack.
Think of Inertia.js as an adapter that connects two devices that weren’t made to work directly with each other: instead of having to create a REST API, Inertia.js allows developers to connect a React, Vue, or Svelte frontend with their Laravel backend.
Say what you want about PHP, but it comes with a lot of tooling right out of the box. Yes, there are trade-offs when using a stack like this instead of an all-JavaScript stack, but it’s a great way to build a powerful monolith — this way, we can have both the benefits of PHP on the backend and a JavaScript framework on the frontend.
The developer only needs to look over a few lines of code in App.js
to get React and Laravel to talk to each other.
Before the Laravel team made it easy to spin up the Inertia-React stack, developers had to do a lot of manual work to get Inertia working with React, as Vue was the default.
Now, the developer will not have to write anything at all, as scaffolding is done automatically by running php artisan breeze:install react
.
The App.js
file will look something like this:
js require('./bootstrap') // Import modules... import React from "react" import { render } from "react-dom" import { InertiaApp } from "@inertiajs/inertia-react" import { InertiaProgress } from '@inertiajs/progress' const el = document.getElementById('app') render( <InertiaApp initialPage={el ? JSON.parse(el.dataset.page) : "{}"} resolveComponent={(name) => require(`./Pages/${name}`).default} />, el ); InertiaProgress.init({ color: '#4B5563' })
For a developer that is already steeped in the world of JavaScript, there is virtually no barrier to entry if they have some knowledge of PHP and Laravel. The Inertia docs are pretty easy to read and cover just about every use case you need to build your app.
In the past, when developers wanted to have a Laravel backend and a JavaScript SPA, they had to build an API with Laravel and use a JavaScript framework that was hosted separately. Now, developers can just use Inertia.
The way it works is pretty genius: the first visit to the page loads pure HTML, and then data is loaded to the page — without a full reload by way of XHR and JSON. Inertia also removes the need for a REST API and gives developers the ability to build a big and beautiful SPA with a React frontend and PHP backend.
It also makes it extremely simple to pass data straight from your database to the client, removing the need for extra frontend libraries like Axios that other frameworks need to do the same thing.
To put this into perspective, let’s compare the way data is sent from the client to the backend with a Laravel API and an SPA built with Next, and the new way we can do this with Inertia.
First, the developer installs and sets up something like Laravel Sanctum to ensure that requests are authorized with tokens, cookies, or some combination. Then, they would need to install and set up the CORS package to prevent CORS issues.
When that is set up, including the middleware to prevent the need for CSRF protection on the frontend, routes are set up in routes/api.php
.
So, let’s say we need a route to create a bank account. Our route would look something like this, where createAccount
is the controller method that will handle the request from the frontend:
php Route::post('create-account', [AccountController::class, 'createAccount']);
Then in the Next SPA, extra work needs to be done to ensure CORS and authentication issues don’t happen.
Frontend developers should be very familiar with CORS issues, and they will most likely come up when the frontend and backend are hosted separately. To solve these issues and handle cookies and other factors, developers end up installing an authentication library such as NextAuth.js or next-iron-session.
When all of that is set up, the function to create the account will use fetch
or axios
to submit the data and wait for a response from the API. That function would look something like this:
js import axios from 'axios' … const [account, setAccount] = useState({ phone: "", street: "", unit: "", city: "", state: "", zip: "" }) async function handleSubmit(){ try { const accountData = JSON.stringify(account) const response = await axios(`${apiUrl}/create-account`, accountData, { header: { 'Authorization': `Bearer ${user.token}`, } }) console.log(response.message) } catch(e){ console.log(e.errors) } }
That’s a lot of work!
With Inertia, there is no need to install extra libraries or write so many lines of code to handle CORS issues and authentication.
The only thing the developer has to do is share data between Laravel and Inertia, so that data is sent with subsequent renders after the first visit, set the route, and use Inertia visits to submit and get data.
Inertia visits are basically at the core of how Inertia works: when we click an Inertia <Link />
or do this programmatically with manual visits (more on them below), the library performs an XHR instead of a page reload. JSON is returned from the server and the client-side of Inertia swaps out the old data with the new.
There are different ways to share data between Laravel and Inertia, but personally, I like to use flashed messages. To do this, I simply place a few lines in app/Providers/AppServiceProviders.php
:
php <?php namespace App\Providers; use Illuminate\Support\Facades\Session; use Illuminate\Support\ServiceProvider; use Inertia\Inertia; class AppServiceProvider extends ServiceProvider { ... public function boot() { Inertia::share('flash', function(){ return [ 'message' => Session::get('message') ]; }); } }
The route can look the same as above, and I can use manual visits in my frontend where methods such as onStart
, onSuccess
, and onError
help to perform events before, during, and after the exchange of data.
Manual visits mimic Promises, but make creating and handling events better than chaining then
statements. Let me demonstrate:
js const [account, setAccount] = useState({ phone: "", street: "", unit: "", city: "", state: "", zip: "" }) function handleSubmit(){ const accountData = JSON.stringify(account) Inertia.post(`create-account`, {data: accountData}, { onStart: () => { // Do something the moment request is made }, onSuccess: response => { console.log(response.message) }, onError: e => { console.log(e.errors) } }) }
Though Inertia is supposed to be framework agnostic, there is first-party support for Laravel and Rails on the backend and React, Vue, and Svelte on the frontend.
If you’re coming from the Gatsby or Next world, you already know how complicated authentication can be. Even when building a simple library, you’ll still have to spend a lot of time setting up login and registration pages, tokens, cookies, email verification, password resets, and routes, among other things.
The most beautiful thing about Laravel is their Starter Kits, which is a large part of how they make authentication so easy. With Laravel Breeze, you can build a system for login, registration, password reset, email verification, and password confirmation with just one command!
If you choose the Inertia-React stack, login, registration, and dashboard pages with their corresponding routes are already done up for you! You can also choose to implement OAuth by extending Breeze using Laravel Passport.
This is a huge advantage over other methods because you don’t have to use libraries to handle complex session and state management for authentication to work properly. With Laravel, everything you need for authentication comes right out of the box.
Session and state management for large apps in React is excruciating without using any libraries or packages. Still, handling state in React is important for session management in Next and Gatsby.
Laravel makes session and state management so much easier. For sessions, Laravel provides you with several ways in which you can store sessions, including:
You can even use your own custom session drivers. From there, interacting with and saving to the session can be achieved with just two lines of code.
The Inertia-React stack of Laravel Breeze further negates the need for any state management on the client side, making for a complete and pleasurable experience when building auth systems or other features that need complex state management.
You also have more complex abilities to control how your Laravel app handles sessions, such as limiting the number of HTTP requests that can be made at the same time.
The code to do this in Laravel is really reminiscent of async-await
in JavaScript. When a request is made, a “session lock” is acquired so that subsequent requests with the same session ID will have to wait for the first session to finish executing before they can execute.
If you look at the code sample below, you’ll see the block method accepts two arguments:
If a session lock takes too long to be acquired, an exception is thrown. It’s a genius way to circumvent PHP’s asynchronous limitations.
php Route::post('/profile', function () { // })->block($lockSeconds = 10, $waitSeconds = 10) Route::post('/order', function () { // })->block($lockSeconds = 10, $waitSeconds = 10
Just like Gatsby and Next, Laravel uses webpack for compiling client-side assets. Configuring webpack is not an easy task — but Laravel has a fix for that in the form of Laravel Mix.
Laravel Mix makes it easy to implement all kinds of tools and technologies for your frontend. It does this by providing an API to dictate the build steps to compile these assets.
Don’t believe me? Below is what a Laravel Mix file looks like in a project using Tailwind (and PostCSS modules), TypeScript, and React:
js const mix = require('laravel-mix'); mix.ts('resources/js/app.tsx', 'public/js') .react() .postCss('resources/css/app.css', 'public/css', [ require('postcss-import'), require('tailwindcss'), require('autoprefixer'), ]) .webpackConfig(require('./webpack.config')) if (mix.inProduction()) { mix.version() }
The code above tells Laravel Mix to look in resources/js
for the App.js
or App.tsx
file and compiles its contents to public/js/app.js
, the JavaScript file that is read by the web browser. Chaining the react()
helper lets Laravel Mix know to expect React and JSX — there’s also a vue()
helper, if you’re using Vue.
This code also tells Laravel Mix to use PostCSS to compile the CSS in resources/css/app.css
, which are Tailwind directives, to actual CSS and place it in public/css
. If the developer wants to set up an alias for paths, they can do so in the webpack config file.
Just like Gatsby and Next, you don’t have to stick with the Laravel Mix/webpack default. If you want to use Vite, Esbuild, or any other similar build tools, Laravel has instructions for that, too.
Both Next and Gatsby have a pages
folder, inside of which you can place files that correspond to the pages of the app. Both use a routing API that lets you use brackets (curly with Gatsby, or square with Next) as the file name to denote dynamic pages.
Both frameworks try hard to make routing easier and more intuitive, but sometimes they need a lot more fiddling to work properly. And since complex business logic is often handled in these files, readability and good code organization often suffer.
Laravel is built with a model-view-controller (MVC) architecture, so it has routes that direct requests from your view on the frontend to your controllers. The MVC architecture forces good code organization practices, as you know your logic will be based in your controllers and your client is sending requests through routes that channel responses back to the client.
The Inertia-React stack handles routing on the server-side, which is different from SPAs built with other frameworks. Routes are found in the routes
folder, and in that folder you can find web.php
, where most of your routes will be housed. All JavaScript files — including the project’s React pages, components, etc. — can be found in the resources
folder.
See how a sample Laravel Breeze and React project with the resources
and routes
folder is set up below:
Gatsby is very opinionated about how data should be handled and dictates that GraphQL be used in most cases. That’s great for devs that love GraphQL, but can be a bit cumbersome for those who don’t. Next is not as opinionated, but devs will still need to install and set up a lot of moving parts to get data from the database to the client.
Laravel is also opinionated with its ORM, Eloquent, but the beauty of the framework is that you can very easily not use it. Instead, you can directly query the database with regular MySQL statements if you need, or you can use another PHP ORM of your choosing.
One popular ORM is Doctrine, which is used often with other frameworks such as Symfony, Zend, and CakePHP. If you want speed and performance, Doctrine is certainly something to consider.
However, if you’re concerned about how well it will blend with the rest of the framework, Eloquent is the best of them all. To understand this, let’s look at the way a database table is created in both Eloquent and Doctrine.
Eloquent
php Schema::create('User', function($table) { $table->id(); $table->string('name'); });
Doctrine
php <?php use Doctrine\ORM\Mapping AS ORM; class User { private $id; private $name; } ?>
In terms of what databases PHP is compatible with, you will hardly have to worry. PHP has drivers and extensions for a wide variety of database systems such as SQLite, MongoDB, and PostgreSQL, so you’re not stuck with just MySQL.
Laravel helps you to easily set up REST APIs, but you can create GraphQL APIs with Laravel, too! If you do choose a GraphQL API for your app, you have a choice between Rebing’s GraphQL library or Lighthouse.
You can even consume GraphQL APIs — or just about any API you can think of — with just a couple lines of code:
php use Illuminate\Support\Facades\Http; $response = Http::get('http://someapi.com'); dd($response); // dump data
Even though they are built in an opinionated way, Gatsby plugins are excellent and plentiful. Next is extensible, too. Don’t be fooled, though — Laravel’s plugin ecosystem is far from paltry. There is a Laravel plugin for just about everything under the sun, and they are housed in one easy-to-use directory called Packalyst.
Though PHP only uses a few lines of code to implement features that would require hundreds or thousands of lines in JavaScript, for everything else, there are plugins and libraries that can easily be installed in your project.
And, of course, if you have an issue with your project, or if you just want to network with other Laravel devs — especially those that use the Inertia stack — there are thousands of developers that you can follow on Twitter, Stack Overflow, GitHub, and Discord. It’s a kind, welcoming, and non-toxic community that obviously loves the technology it uses and wants others to love this technology, too.
Currently, Inertia.js renders webpages on the client-side. The creators of Inertia.js argue that Inertia wasn’t made for webpages that need SEO, so creators should use Blade for those kinds of pages instead. There are other workarounds such as using meta tags, and some people have developed workarounds.
Don’t let this be a dealbreaker: an SSR mode for Inertia.js is coming very soon. It’s worth mentioning that, at the time of this article’s publication, GitHub sponsors have early access to it. There are also some SaaS apps in production that have been successfully using Inertia.js.
Though I listed it as a pro earlier, to be fair, PHP does fall short to Node.js in some ways, including concurrency, asynchronous requests, and speed. Node’s strength with async processing enables faster build times and increases flexibility around how an app is built. PHP does have some plugins that allow for asynchronous processing, but it’s not the same.
This shouldn’t let you put PHP down, though. Thanks to a newly energized community, the language is adding new features and is already a lot faster and much more flexible than it was in the recent past. And it still handles many things better than Node, such as:
React frameworks like Gatsby and Next can pre-render pages to static HTML, CSS, and JS files. Static site generation has been a growing paradigm recently as Jamstack and serverless architecture adoption have skyrocketed.
As a result of this, developers have been eager to switch to those and other similar frameworks so that they can build full-stack apps by just focusing on the frontend and the way data is fed into the app.
Yet, apps can be broken up into bits and pieces where many functions and capabilities are handled by third-party APIs and microservices. Sure, Laravel Breeze can utilize third-party services too, but the concept that powers Inertia is that building powerful monoliths is the only thing that you need to do.
Why would you want to build a monolith? Here are a few reasons:
If you want to use Laravel but also want to use Jamstack, you could check out other Laravel-based technologies such as Statamic and Jigsaw. With these tools, you get CMS capabilities and the joy of building with PHP, the Blade template, and other Laravel features.
Some developers complain that Laravel does too much “magic” and they don’t have the control that other PHP frameworks have. They’re wrong, though, because Laravel provides all the control a developer needs and the code abstraction helps for a much better developer experience.
This is the reason why Laravel is the most popular PHP framework by far and the most popular backend framework. Also, isn’t a magical feeling the sign of a great framework?
As developers, we use frameworks to make building complex apps easier, and Laravel, especially Laravel Breeze’s Inertia-React stack, makes building complex React apps incredibly easy.
While many app developers go serverless and split their app up into many parts, Inertia has proven that you can build big and powerful React SPA monoliths. For state and session management alone, Laravel Breeze’s Inertia-React stack is worth a try compared to the competition.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowSimplify component interaction and dynamic theming in Vue 3 with defineExpose and for better control and flexibility.
Explore how to integrate TypeScript into a Node.js and Express application, leveraging ts-node, nodemon, and TypeScript path aliases.
es-toolkit is a lightweight, efficient JavaScript utility library, ideal as a modern Lodash alternative for smaller bundles.
The use cases for the ResizeObserver API may not be immediately obvious, so let’s take a look at a few practical examples.