Contact Us

Laravel 8 + Vue.js 3 CRUD with Composition API – Laravel Daily

Mobile App | May 6, 2022

There are quite a lot of articles/videos on Laravel + Vue CRUD, but not enough is published on the newest Vue.js 3 version, using the new Composition API. So, with this step-by-step detailed article, let’s fill in that gap, building a simple Company management form.

Notice: the link to the final repository is at the end of the article.

Install Laravel and Laravel Breeze

We start from the very beginning, by installing a fresh Laravel project, and a Laravel Breeze starter kit:

By this point, we should have a default Laravel Breeze with Tailwind CSS design, and Login/Register functionality:

Creating Model and API CRUD

We will manage one table called Companies, with four text fields: name, email, address, website.

So, we create the model, and automatically create migrations with -m:

This is the DB structure: database/migrations/xxxxx_create_companies_table.php:

In the app/Company.php model, we make all fields fillable:

Next, we create a Controller, with a few flags to generate exactly what we need:

Personally, I like to use API Resources to transform the data. Although in this project, we won’t make any transformations, I still have a habit of generating them:

And, inside of app/Http/Resources/CompanyResource.php, there’s this default code:

Next, for validation, we generate a FormRequest class:

In this case, I will re-use the same validation rules for both store/update functions, so this is the content of app/Http/Requests/CompanyRequest.php:

We use those API Resource and Form Request classes inside of our app/Http/Controllers/API/CompanyController.php, which has this code:

And, we tie it all together to call that Controller from the routes/api.php:

In this simple project, we won’t use any Middleware, the routes are public.

So, at this point, we have an API ready, and if we enter some company manually in the DB, here’s what we get via Postman:

Installing Vue and “Hello World”

Now, we get to the front-end. We install Vue.js, Vue Router and Vue Loader:

If you don’t use @next, it will install the older Vue.js 2 version, this is not what we want in this article, we want to use Vue 3 with Composition API.

Next, we need to enable the vue-loader, and in the webpack.mix.js we need to add the vue() part to be compiled.



Now, we need to create our first Vue.js component. For now, it will not do anything dynamic, just show the “Hello world”, in resources/js/components/companies/CompaniesIndex.vue:

That’s it in this file, for now, we won’t add any <script> element, we’ll get to that part in the next section.

Now, let’s create our routes file. We will have three routes: company index, create, and edit forms. But, for now, let’s stick with the first one, here’s the Vue.js 3 version syntax of resources/js/routes/index.js:

Next, we add the id=”app” in the main Blade file of our project resources/views/layouts/app.blade.php:

Now, we can create our Vue application inside of that #app, here’s the Vue 3 version syntax for it, in resources/js/app.js:

Next, let’s prepare the Laravel view to contain the Vue functionality. We need to add this line to routes/web.php file, which would load the dashboard on any other view coming from Vue Router:

We change the default Laravel Breeze file resources/views/dashboard.blade.php to include the <router-view /> tag:

That’s it, let’s run the main command to compile it all:

So, now, after logging into our Laravel Breeze dashboard, we should see this:

Yay, we enabled Vue.js in our project!

List of Companies: Composition API

In Vue 3, the way to create components was changed, with the introduction of Composition API. It allows you to divide the parts of the component to separate files, to then be re-used. The problem appears in larger projects, but I would advise using it even on smaller projects like this one, to get into the habit of separating the concerns, and make code more readable.

So if you came here with some Vue 2 background, the code below will not look too familiar to you, so please read the official Vue documentation on Why Composition API, and also this article: Vue 3 Composition API vs. Options API.

Notice: you still can use the “old way” of components in Vue 3 as well, but, while preparing for this article, I asked my Twitter audience, and many people with Vue 3 actually do use the Composition API:

To have a CRUD of Companies, we will use a Composition API thing called Composable, which is a separate file that will contain all the methods we need. It’s kind of like a Service in Laravel if you wish.

So, we create resources/js/composables/companies.js, with this code:

A few things to note here.

The usual standard name of the function of Composable is useSomething(), and it doesn’t necessarily need to be the same as the filename.

Next, we use axios for the API requests, which is by default included in Laravel installation, we just need to import it and can make the requests like axios.get() and

Next, what is that ref thing? It is shorter for “reference”, and as it is said in the official Vue 3 documentation, “In Vue 3.0 we can make any variable reactive anywhere with a new ref function”. So, when we define our variables with, for example, const companies = ref([]) syntax, now whenever we call getCompaniescompanies will be mutated and the view will be updated to reflect the change.

The next thing to notice is that we use Vue Router to redirect to the list, by its name companies.index, after a successful store/update method. In case of validation errors with code 422, we parse the error structure and turn it into a String variable of errors.

Finally, we define what we return from the Composable file – all variables and methods.

Now, let’s fill them into our resources/js/components/companies/CompaniesIndex.vue with the actual table of data about the companies. Here’s our updated code, the table in the <template> part, and the Composition API in the <script> part.

The table doesn’t need much comment, it’s just doing the foreach loop, with this syntax:

Now, how do we get the companies variable? I will add some comments with the script part:

And, after running npm run dev (or, again, if you have npm run watch active), we see this on the dashboard:

See, the Composition API isn’t that difficult? Now, let’s add one more method to it.

A Button to Delete Company

Contrary to popular CRUD articles where Delete comes last, I want to implement it immediately now, cause it will be short and will demonstrate the Composition API power.

In the Composable file resources/js/composable/companies.js we add one more method that calls the Delete API endpoint. Also, we need to return that method at the bottom:

Now, we can call that method from our component. So, in the resources/js/components/companies/CompaniesIndex.vue we add a button in the template, with the method in the script:

The template part is pretty clear, we just add button @click=”deleteCompany(” there.

The script part is more interesting. We add destroyCompany to be fetched from the Composable file, we also define a local method deleteCompany, which in turn calls that composable method of destroyCompany, and then calls to getCompanies again, to refresh the table. So, see, how we re-use the Composable file, without polluting the main component? This is one of the benefits of Composition API.

Also, see that we need to list the deleteCompany in the return of setup() method, otherwise it wouldn’t be visible in the template part, for our button.

That’s it for this section, we’ve made the delete button working!

Create Company: Route, Form, Validation

Next, above the table, let’s add a button that would lead to the form for creating a new company. For now, let’s create an empty component in resources/js/components/companies/CompaniesCreate.vue:

Next, we add a route to the resources/js/router/index.js:

Finally, we add a button above the table, with router-link, in the resources/js/components/companies/CompaniesCreate.vue:

Notice, that we call the route by its name – the same one that we defined in the routes file.

So, for now, our button looks like this:

It leads to a page that is empty for now:

Now, we will fill it with the form and script to process that form, here’s the full code for resources/js/components/companies/CompaniesCreate.vue:

In the template section, the most important parts are <form @submit.prevent=”saveCompany”> and then each field has v-model=”form.[field_name]”, binding it to the form object.

That form is described below, in the script section, with default values:

The reactive thing, which we need to import with import { reactive } from ‘vue’, is very similar to the ref that we discussed above, but the inner value should be dictionary-structured data like JS object instead of a single value.

Next, the saveCompany() method will be a local component method, which, in turn, calls the Composable method of createCompany().

In case of validation errors, we have errors string with all the errors, separated by a space symbol. The result looks like this:

And if all goes well, we are automatically redirected (but without reloading the full page) to the list of companies. It is done in the Composable file, by router.push({ name: ‘companies.index’ }).

Company Edit/Update Form

The edit form will be almost identical to the edit form, with just a few differences.

First, the empty component resources/js/components/companies/CompaniesEdit.vue:

Next, the route in resources/js/router/index.js – this time with a parameter :id and with a setting props: true:

Then, a button to edit a particular record in resources/js/components/companies/CompaniesIndex.vue:

Here’s the visual result:

And, when we click Edit, we see this, as expected:

Now, let’s build the actual form. Here’s the code of resources/js/components/companies/CompaniesEdit.vue:

It is very similar to the Create form, but here are a few differences.

First, we accept the props and define that we expect id as required/string. Then, we use it inside of setup(props) and call the Composable methods with that exact parameter: getCompany( and updateCompany(

Next, in the create form we had a form variable that we defined as an object with keys and empty values. In this case, we actually have a variable company that is saved in the Composable file, so we don’t need to even pass it anywhere, we just get it from useCompanies() and use it as a local one. On the very event of mounted, we call getCompany() but we don’t need to assign the result to a variable, it’s all done in the composables/companies.js. Now, you see the beauty of Composition API?

The submit and validation parts work absolutely identical as the create form: redirects to the index in case of success, or show the error in case of the validation error.

The link to the final repository:

Of course, it’s a very simple example: with only text fields, with no fancy design styling elements, with just core basics. But that was exactly the point – to give you a head start, from here you can read more about Vue and API, and practice more complex scenarios.

If I forgot/missed something in this article, or if you have any ideas for future articles on this topic, let me know in the comments.

Like our articles?
Check out our Laravel online courses!

This content was originally published here.