Editor’s note: This article was edited and updated in September 2021 to include a comparison of Livewire with Inertia.js, which is the new way to leverage a Vue.js frontend with a Laravel backend.
Blade is an awesome templating language that allows us to make full-scale, dynamic Laravel app views. However, many modern apps demand page components update without page reloads, and it is cumbersome to do this with traditionally-made Laravel apps that use Blade and JavaScript libraries like jQuery.
On the other hand, single-page applications (SPAs) built with JavaScript frameworks such as React, Svelte, and Vue are known to do this with no hassle.
Thanks to contributors in the Laravel community, there are now a few ways in which we can build these types of applications. In this article, we’ll discuss two of the most popular frontend technologies that are used to build modern web apps with Laravel: Livewire and Vue.
To follow along with this tutorial, you’ll need:
Since Laravel 8, there are now two main ways in which authentication and the frontend in Laravel applications are scaffolded: Laravel Jetstream and Laravel Breeze.
With Jetstream, we can scaffold Laravel apps with authentication, email verification, password reset system, session management, frontend with Tailwind CSS, etc. There are also two frontend technologies that we can choose to build these apps with: Inertia.js and Livewire.
Laravel Breeze is somewhat similar, but is a much simpler way to set up an application with auth and frontend scaffolding. The other difference is that Breeze only comes with Inertia.js.
Inertia.js is a JavaScript library that connects a JavaScript frontend to a different backend without the need for an API. Apart from Vue, Inertia.js allows us to connect React and Svelte frontends as well.
So, instead of reaching for heavily complex React frameworks — such as Gatsby and Next.js, and Vue frameworks like Nuxt.js — to handle data-fetching, data-handling, and routing on the server, we can use the magic of Laravel on the backend.
Laravel Livewire promises the SPA experience that comes with React and Vue but without all of their complexity. Livewire components make AJAX calls to the server and though the response is HTML, instead of reloading the whole page with the new HTML, Livewire mutates the DOM with the changes.
In this article, we’ll be talking about the context of using Jetstream and Breeze. However, you should know that you don’t need to use Jetstream or Breeze in order to use Livewire or Vue.
Vue already comes pre-installed in Laravel, and all you’ll need to do is use Laravel Mix to compile the Vue components into a single JavaScript file that is browser-ready.
For Livewire, all we’ll need to do is run composer require livewire/livewire
and include the directives in Blade. However, in this context, we’ll be using the scaffolding provided by starter kits such as Laravel Breeze and Jetstream.
To install Livewire with Jetstream, Jetstream must first be installed into a fresh Laravel installation by running:
composer require laravel/jetstream
Afterwards, run php artisan jetstream:install livewire
to install a variety of components associated with Livewire.Then, run php artisan vendor:publish --tag=jetstream-views
to publish and use Blade components with Livewire, such as inputs, modals, and buttons.
Frontend assets will still need to be installed by running npm install && npm run dev
. The database will also need to be migrated by running php artisan migrate
.
To install Inertia with Jetstream, Jetstream must be installed into a fresh Laravel installation by running:
composer require laravel/jetstream
Inertia can be installed by running php artisan jetstream:install inertia
. Combine the frontend assets by running npm install && npm run dev
and migrate the database by running php artisan migrate
.
To get Inertia installed with Jetstream we must first install Laravel Breeze by running composer require laravel/breeze --dev
.
Afterwards, install Breeze’s components by running:
php artisan breeze:install vue
Breeze also gives us the option to use React, but we won’t be talking about that today.
Install and combine the frontend assets by running npm install && npm run dev
. The database will also need to be migrated: php artisan migrate
.
Inertia handles components and templating very differently from Livewire because Inertia is responsible for helping a PHP backend “talk” to a JavaScript frontend, while Livewire extends features that are already available in Laravel.
For example, let’s consider a simple counter feature where we’ll need to increment by one every time a button is clicked.
With Livewire, we create a Counter
component where we initiate our $count
property and have a render()
method that loads the view.
<?php use Livewire\Component; class Counter extends Component { public $count = 0; public function render() { return view('livewire.counter'); } public function increment() { $this->count++; } }
We then embed Livewire into our view by including this line in our Blade file: @livewire('counter')
. With the wire:click
directive, we can then bind the click
event so that we can run the method on the backend whenever the event is triggered on the frontend.
<div> <button wire:click="increment">Count</button> <span>{{ $count }}</span> </div>
The fact that we don’t have to write a line of JavaScript to get interactive components is very attractive to many PHP developers who are not fans of writing frontend code. Livewire developers also have the added luxury of sticking with Blade and having access to all its directives and features.
For Laravel developers that don’t mind delving into JavaScript-land, Inertia makes creating JavaScript SPAs very simple. On the backend, we can send data to the frontend through the controller/route and the use of methods and helper functions. In the example below, we have our CounterController
, which has a $counter
property that holds our increment value and our increment()
method.
<?php namespace app\Http\Controllers; use Illuminate\Http\Request; use Inertia\Inertia; class CounterController extends Controller { public $counter = 1; public function index(){ return Inertia::render('Home/Index', [ 'counter' => $this->counter, ]); } public function increment(Request $request){ $count = $this->counter + $request->count; return Redirect::route('home', ['counter' => $count]); } }
Before we move forward, let’s put it out there that having a counter app that talks with a backend would be overkill. Vue already has the tools in place to do this on its own. But for demonstration purposes, you’ll see two buttons below: one that increments via Vue alone, and one that increments with the help of Inertia and our backend.
<template> <button @click="count++">Via Vue: {{count}}</button><br> <button @click="countup">Via Inertia: {{total}}</button> </template> <script> export default { data(){ return { count: 0, vuecount: 0, total: this.total } }, computed: { total() { this.vuecount = this.vuecount + this.$page.props.counter return this.vuecount } }, methods: { countup() { this.$inertia.post('/counter', {count: this.vuecount}) } } } </script>
Before Inertia, the only way to share data with a Vue frontend was through a REST API. Now, how data is shared and accessed in the frontend doesn’t have to be a major consideration when choosing between Inertia and Livewire. The only issue is that more lines of code will need to be written on the frontend.
There is a huge difference between the ways Livewire and Inertia render data. All requests for Livewire are rendered on the server side, while all requests for Inertia are rendered on the client side. Both have their advantages and disadvantages.
With client-side-rendered apps, requests are redirected to a single empty HTML file, where JavaScript compiles everything. These apps are usually very fast, but the disadvantage is that SEO sometimes suffers (search engines like Google are still able to crawl JavaScript files).
With server-side-rendered apps, the markup and the data is compiled on the server before it is served to the client. This is the way the web has worked since the beginning, but the problem is that this method is slower.
Even though Livewire and Inertia both have their weaknesses, there are solutions to cover for them. In Livewire’s case, components can be cached after the first render. For Inertia.js, an SSR solution is currently being built where the page will be rendered server-side on the first load, but subsequent requests are done client-side.
Reactivity describes the way modern applications are dynamic and change based on user interaction. Livewire and Vue handle reactivity quite differently.
Vue handles reactivity by tracking the changes to specific variables and re-rendering the affected parts of the DOM. When creating Vue components, you define variables for Vue to track (reactive data). Here’s an example with one reactive variable, message.
<template> <h2>{{message}}</h2> </template> <script> export default { data() { return { message: "Hello World!" } } } </script>
In the above example, Vue tracks every change made to our example message and will update h2
appropriately. These updates are happening on the client side — it does not need to interact with the backend because the state of this component lives in the browser.
However, there are times when the state in the browser needs to be in sync with data from the database, in which case AJAX will update both the frontend and the backend to be in sync with each other. The problem is, if something happens and an update fails, which causes the frontend and backend to fall out of sync, problems could arise.
By contrast, the state of a Livewire component resides on the server. This, similarly, has its advantages and disadvantages.
<?php namespace App\Http\Livewire; use Livewire\Component; class HelloWorld extends Component { public $message = "Hello World"; public function render() { return <<<'blade' <div> {{message}} </div> blade; } } ?>
For simple components like the one above, there is no effect of the state being stored on the server. However, for dynamic components that update frequently based on user interaction, several AJAX requests have to be made to update the state in the server.
This may cause some performance issues. The developers of Livewire propose that to combat this, developers can use “dirty states”.
In this guide we explored the differences between Livewire and Vue and highlighted areas in which they both shine and fail.
To be fair, currently, your choice of framework mostly will come down to whether you like working with JavaScript or not. In most use cases, Livewire provides the same power and dynamism as JavaScript apps.
Vue is already built for reactivity, but Livewire lives on the browser, so SEO won’t be a problem if you want to be on less-advanced search engines than Google. Laravel developers are spoiled for choice.
Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens in your Vue apps, including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error and what state the application was in when an issue occurred.
Modernize how you debug your Vue apps — start monitoring for free.
Hey there, want to help make our blog better?
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 nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.