I want to start this course by demonstrating Livewire to you. Its purpose is to create dynamic pages with no browser refresh but without writing JavaScript. So, let's start with a simple example of submitting the form and showing the success message on the same page, clearing up the form fields.
Let's start by installing Livewire into a Laravel project and creating the first Component.
By the of the lesson, you will know how to:
- Install Livewire and create a Livewire component.
- Use component properties and bind them into a form.
Before Starting
This course is about Livewire. In this course, for simple styling, I am using Tailwind CSS. You can find how to set up Tailwind CSS in the official documentation.
If you are using Breeze starter kit, it adds AlpineJS, which will conflict with Livewire v3 because Livewire v3 is bundled with AlpineJS. You need to remove AlpineJS from Breeze, then. How to do it you can read in the post Livewire 3 and Laravel Breeze Error: Alpine.js Conflict.
We have made a skeleton app based on Breeze, which you can use to quickly have Breeze-style pages without authentication.
Another option for quick scaffolding is to use Laravel Jetstream. Version 4 comes with Livewire 3 out-of-the-box.
If you will be using Full Page Components then you need to set different path for layout. This can be done globally in the config file.
config/livewire.php:
// ...'layout' => 'layouts.app',// ...
Install Livewire and Create First Component
In the Terminal, we run this:
composer require livewire/livewire:^3.0
Now that we have installed Livewire let's create a Component and call it CreatePost
.
php artisan make:livewire CreatePost
This command creates two files:
- a PHP class:
app/Livewire/CreatePost.php
- and a Blade View file:
resources/views/livewire/create-post.blade.php
Now that we have a Livewire component, we need to render it inside the Laravel Blade file. You can do that using the <livewire:component-name />
syntax.
So, to render our CreatePost
component, we need to add <livewire:create-post />
where we want to show the content of it.
For example, we have this Route in Laravel:
routes/web.php:
Route::view('posts/create', 'posts.create');
And then we have this Blade file:
resources/views/posts/create.blade.php:
<html><body> // ... full layout header <livewire:create-post /> // ... full layout footer</body></html>
For a simple demonstration that it works, let's add a static text to the Livewire component in the resources/views/livewire/create-post.blade.php
Blade file.
resources/views/livewire/create-post.blade.php:
<div> Create Post Livewire Component</div>
Important: The Livewire component needs to have a root
<div>
HTML tag.
Now, visit the page /posts/create
where your Livewire component should be rendered, and you will see that static text from the Component.
Great, our component is loaded!
Properties and Data Binding
Now, let's add a simple form instead of that static text.
resources/views/livewire/create-post.blade.php:
<div> <form method="POST"> <div> <label for="title" class="block font-medium text-sm text-gray-700">Title</label> <input id="title" class="block mt-1 w-full border-gray-300 rounded-md shadow-sm" type="text" /> @error('title') <span class="mt-2 text-sm text-red-600">{{ $message }}</span> @enderror </div> <div class="mt-4"> <label for="body" class="block font-medium text-sm text-gray-700">Body</label> <textarea id="body" class="block mt-1 w-full border-gray-300 rounded-md shadow-sm"></textarea> @error('body') <span class="mt-2 text-sm text-red-600">{{ $message }}</span> @enderror </div> <button class="mt-4 px-4 py-2 bg-gray-800 rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700"> Save </button> </form></div>
The form should look similar to this:
So now that we have a form, each input field should have a public property inside the Livewire PHP class.
app/Livewire/CreatePost.php:
use Livewire\Component;use Illuminate\Contracts\View\View; class CreatePost extends Component{ public string $title = ''; public string $body = ''; public function render(): View { return view('livewire.create-post'); }}
Now, we need to bind those properties to the inputs in the Blade file using the wire:model
directive.
resources/views/livewire/create-post.blade.php:
<div> <form method="POST"> <div> <label for="title" class="block font-medium text-sm text-gray-700">Title</label> <input id="title" class="block mt-1 w-full border-gray-300 rounded-md shadow-sm" type="text" /> <input id="title" wire:model="title" class="block mt-1 w-full border-gray-300 rounded-md shadow-sm" type="text" /> @error('title') <span class="mt-2 text-sm text-red-600">{{ $message }}</span> @enderror </div> <div class="mt-4"> <label for="body" class="block font-medium text-sm text-gray-700">Body</label> <textarea id="body" class="block mt-1 w-full border-gray-300 rounded-md shadow-sm"></textarea> <textarea id="body" wire:model="body" class="block mt-1 w-full border-gray-300 rounded-md shadow-sm"></textarea> @error('body') <span class="mt-2 text-sm text-red-600">{{ $message }}</span> @enderror </div> <button class="mt-4 px-4 py-2 bg-gray-800 rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700"> Save </button> </form></div>
That binding means that whenever the input value changes, the Livewire component variables will receive those new values, and then you may perform PHP operations with them.
Action Methods: Submit Form Data
Now, let's add an action to save a post to the DB. For that, we create a method inside the Livewire PHP class. This method can be called whatever you want. We will just name it save()
.
app/Livewire/CreatePost.php:
class CreatePost extends Component{ // ... public function save(): void { dd('save'); // Just for demonstration, for now } // ...}
For now, we hardcode the text just to show that it works.
Now, we need to call this method from Blade upon pressing the Save
button. This is done by using the wire:submit
directive and specifying the method.
resources/views/livewire/create-post.blade.php:
<div> <form method="POST"> <form method="POST" wire:submit="save"> // ... </form></div>
Now, after clicking the Save
button, we will have a dd()
message save
.
This means the button works! Now, let's replace the dd()
with saving a post to the DB.
app/Livewire/CreatePost.php:
use App\Models\Post; class CreatePost extends Component{ // ... public function save(): void { dd('save'); Post::create([ 'title' => $this->title, 'body' => $this->body, ]); } // ...}
After clicking Save, we should see a new post if we check the posts
table in the DB.
Form Validation
Currently, our form doesn't have any validation after submitting, so let's add a few rules.
One of the ways to add them, released in the Livewire 3, is to use PHP 8 syntax of attributes on top of each property variable.
app/Livewire/CreatePost.php:
use Livewire\Attributes\Rule; class CreatePost extends Component{ #[Rule('required|min:5')] public string $title = ''; #[Rule('required|min:5')] public string $body = ''; public function save(): void { $this->validate(); Post::create([ 'title' => $this->title, 'body' => $this->body, ]); } // ...}
And that's it. That is how easy it is to add validation in the Livewire component. And all Laravel validation rules also work here.
After clicking the Save
button, you will see validation error messages.
Again, to reiterate: this happens without a page refresh but without writing any JavaScript. That's the beauty of Livewire.
Show Success Message and Clear Form
Now, after saving the post, let's show a success message above the form and clear the form.
First, the success message. For this, we need a new boolean
property $success
, with a false
default value.
class CreatePost extends Component{ #[Rule('required|min:5')] public string $title = ''; #[Rule('required|min:5')] public string $body = ''; public bool $success = false; // ...}
In the Blade file, we need to make a simple if-check to see whether $success
is true
, and then the message will be shown.
resources/views/livewire/create-post.blade.php:
<div> @if($success) <span class="block mb-4 text-green-500">Post saved successfully.</span> @endif // ...</div>
Notice. I'm not sure if you fully understand by now, but I will reiterate: each property of the Livewire class is automatically available in the Blade file as a variable. There is no need to pass them manually.
All that is left is to set success
to true
and reset the form.
app/Livewire/CreatePost.php:
class CreatePost extends Component{ // ... public function save(): void { $this->validate(); Post::create([ 'title' => $this->title, 'body' => $this->body, ]); $this->success = true; $this->reset('title', 'body'); } // ...}
The method $this->reset()
sets all the passed properties to their default values, which are empty strings in our case.
After the post is saved, we see a success message.
Great, it all works!
So, does it give you an overview and "the first feeling" of Livewire? To reiterate, it gives you the opportunity to process form input variables without refreshing the page, but it all happens in a PHP/Laravel code familiar to us. No JavaScript to write. At all.
Curious to find out more? Let's dive deeper!
For some reason the reset did not work however the success message did appear and it was saved in the database. I did copy and past you code in.
By the way hear is the composer json and package json content
PS. Every thing has been working up to this point.
You are using breeze. https://twitter.com/PovilasKorop/status/1696879703187984583?t=U_1RCFwDpuFymFTwPDLL_w&s=19
that did not fix a thing It still will not work.
Did you run
npm run dev
orprod
after removing alpine. Any console errors?I am using vite. however I restarted it and I am still getting the same thing and I am getting No errors.
Please did your find a solution for the issue of
$this->reset()
not working ? Having the same errorThe reason is in your setup. If you are using breeze then you need to remove alpine and compile assets using
npm run prod
I've got the same issue as Richard A. Hoyle. Everithing works but the reset. I've notice that even the input text content is there, if you try to save again you'll have a message as if the text was empty!
Read Povilas tweet that was mentioned here already
Thank you but I'm not using Alpine / Breeze. Have any other sugestion?
{ "private": true, "type": "module", "scripts": { "dev": "vite", "build": "vite build" }, "devDependencies": { "axios": "^1.1.2", "laravel-vite-plugin": "^0.8.0", "vite": "^4.0.0" } }
Still it's gonna something about your setup. Maybe you are adding alpine some other way. Hard to tell. Check console errors.
I start to use Jetstream and all is working now.
Thanks
Thats one way. But it would be better to find problem which will help in the long
Thought I would try this out in Jetstream and guess what it is working perfectly the inputs are reset and the info is saved to the database Grate! This is the new Jetstream 4 with livewire 3
Ots bec it uses the v3 livewire. It means earlier it was problem with how you set up your project. This course isn't about using anything besides livewire
Following this tutorial, I am stucked at loading the first component. Teacher says "Now, visit the page where your Livewire component should be rendered, and you will see that static text from the Component." We havn't created any route yet so what url should I use in my browser? I am on localhost:8000/....
OK, I created this route:
and a view/components/layout.blade.php: https://laravel.com/docs/10.x/blade#layouts-using-components then changed in the livewire config: 'layout' => 'components.layout', finaly I changed posts/create.blade.php
Thanks for the comment. I didn't think I needed to explain the route, as it could be any route that leads to the Blade file, but maybe you're right as it's not perfectly clear.
I added a few lines of explanation with a simple route:
Hey Povilas,
Great course as usual!
Would you consider committing each lesson to the Git repository as you complete it, so that we can follow along more easily lesson-by-lesson? Maybe even add each lesson in several commits, if there are significant stages during the lesson where things change?
Also great "lesson zero" would be setting up the project with commits showing the initial commit of the empty Laravel framework, then subsequent commits showing each package being added, such as TailwindCSS, TailwindCSS/Forms, Livewire 3, etc. Much can be learned quickly on how to create a new project from such a concise first set of commits. And it would mean that we all start out with something which actually looks like your screenshots ;o))
Thanks again.
I tried to do that, but during the creation of the lesson there are so many little changes that it's extra hard to keep it all in one commit.
The purpose of my lesson is exactly that you would practice on something that does NOT look like my screenshots, as the course is NOT about Laravel/Tailwind, so my goal is that it would fit anyone using Livewire 3, even if they use Bootstrap or other design.
But maybe for the future I would reconsider it, maybe showing one path in full from the beginning, repeating all the fundamentals, is a better way to teach. Will think about it, thanks for the opinion!
@livewireStyles @livewireScripts didint work form me, the create post from appears without css
I don't understand why adding these form should be styled. It's livewire styles and v3 doesn't even need it. In this course we are using tailwind for styling.
Did you used the Tailwind CLI tool or did you include tailwind by using CDN in the header?
Used breeze to be quicker. But in this course it doesn't matter what you will use