Setting up a project in Laravel with Vue 3 in Mix and Vite.js

Video thumbnail

Content Index

Laravel and Vue.js are one of the most powerful combinations in modern web development. Laravel, as a PHP backend framework, offers robustness and structure; while Vue, from the frontend, provides dynamism and a reactive architecture that improves the user experience. Integrating both is not only possible, but also simple if you understand how their layers connect.

The scheme we saw earlier for handling user preferences from a JSON field is ideal when using Vue, since internally using JavaScript, JSON is the best ally.

In this article, I'll show you how to integrate Vue 3 into Laravel using Vite (the current standard) and also Laravel Mix, for those working with older versions. Additionally, I'll share how I modularized my components in a real project to keep them reusable and scalable.

Why Use Vue with Laravel?

Vue.js and Laravel naturally complement each other. Laravel uses Node.js for asset compilation, which allows the incorporation of modern client-side frameworks without complications. Vue integrates perfectly thanks to its simplicity and tools like Laravel Vite Plugin or Laravel Mix.

In my case, this integration allowed me to build more interactive applications without abandoning the PHP ecosystem. For example, I managed to reuse the same Vue component in several sections of my application (courses, books, and packs) without duplicating code.

Laravel Inertia vs Laravel + REST API + Vue/React: When to use each one

Video thumbnail

If you work with Laravel and Vue, sooner or later you ask yourself this question:

Should I use Inertia or set up a REST API directly?

It is not a theoretical doubt. It is an architectural decision that affects development time, maintenance and, above all, how easy (or difficult) it will be to evolve the project in the future. I have been asked this several times and I have personally gone through both approaches in real projects.

In this article, I am not going to repeat textbook definitions. We are going to see what changes in practice, what problems each option avoids, and when a seemingly "convenient" decision ends up being expensive.

The real dilemma: Inertia or REST API in Laravel projects

Why this doubt appears in almost all projects with Vue

  • The Laravel + Vue combination is very powerful, but it presents a clear fork in the road:
  • Staying in a comfortable monolith using Inertia

Separating backend and frontend with a classic REST API

Inertia is especially attractive because it eliminates friction: there is no need to design endpoints, manage tokens, or duplicate routes. Everything flows. And that is precisely why many people adopt it without thinking too much about the medium-term scenario.

The mistake of considering the decision only for the short term

The problem is that most comparisons focus only on "what is faster now," and almost no one talks about the cost of changing the decision later.

That is where the real problems begin.

Practical example

For example, in a project I have with Laravel Inertia, in the detail view we receive contact_general_id as a prop. If we look at the controller, we will see that this data is sent directly to Inertia, without the need to consult a Rest API to fill the information. This works the same whether it is a prop or a variable defined in the data section; the important thing is that the data reaches the component.

A very common query is: "If I want to make a request to get certain information, should I use Axios or Rest API?" The answer is: not necessarily.

How Laravel Inertia works in practice (without REST API)

Direct communication between controller and Vue component

Inertia works as a direct bridge between Laravel and Vue. Instead of returning a Blade view, the controller returns a Vue component along with the data.

There is no intermediate endpoint, no "public" JSON:

Laravel talks directly to Vue.

Props, state, and navigation without Axios or fetch

A very common question is:

“If I need new data, do I use Axios?”

In most cases, it is not necessary. With Inertia you can render lists, forms, and complete views by passing data from the backend, without firing extra requests from the frontend.

This greatly simplifies development:

  • Less code
  • Fewer intermediate states
  • Fewer points of failure

What you gain in development speed:

  • Session-based authentication ready from the start
  • Centralized routes in Laravel
  • Fewer technical decisions

For pure web applications, the pace is very high. You advance quickly and with a sense of total control.

What a REST API provides that Inertia does not cover

Real separation between frontend and backend

A REST API forces the definition of a clear contract between backend and frontend. This adds work at the beginning, but it also imposes order.

Each endpoint has a clear purpose. Each response has a defined format. This is especially noticeable as the project grows.

Reusability for mobile, desktop, and third parties

In my Academy project that I developed, I decided to directly create a REST API even though the initial application was web-based.

Thanks to that, when I later migrated the application to Flutter, I already had the entire foundation built. I didn't have to rethink the architecture or redo the business logic. In real terms, it saved me at least 40% of the work.

If I had used Inertia from the beginning:

  • I would have created the web app with Inertia
  • Then I would have had to create another API
  • And duplicate logic, validations, and structures

That is the hidden cost that almost no one mentions.

Total control over responses, errors, and formats

With an API you decide exactly:

  • What errors you return
  • What structure each response has
  • What data you expose and what you don't

This facilitates debugging, testing, and scaling, especially when the frontend is not just Vue.

Inertia vs Axios

When we use Inertia, it is not necessary to make additional requests with Axios or fetch. For example, if we are building a listing component, we can fill the information directly with the props sent from Laravel. This saves time and simplifies communication between the backend and Vue.

Mind you, the response we get when sending data via Inertia can be more limited than that of Axios; we don't have, for example, a full list of form errors. But this is fine, because the goal of Inertia is to avoid implementing a full Rest API and making multiple requests from the frontend.

router.post('/users', form)

Inertia vs REST API: key differences that really matter

Initial time vs future cost

  • Inertia: less friction when starting
  • API: more initial work, fewer surprises later

Inertia speeds up the startup. A well-thought-out API reduces the cost of evolution.

Logic duplication and maintenance

If you know the project will have more than one client (web + mobile, for example), Inertia ends up being an intermediate step that later has to be undone.

And undoing architecture is always more expensive than doing it right from the start.

Authentication, state, and scalability

  • Inertia leverages sessions: convenient, fast, proven
  • API usually uses tokens: more complexity, more control

Neither is "better" in the abstract. It depends on the context.

Testing and debugging

With an API, you can easily inspect each call in the browser's Network tab or from external tools. In my experience, this greatly speeds up development when the frontend is not exclusively Vue.

Thinking about the future of the project: the question almost no one asks

What happens if tomorrow you need a mobile app

This is the key question I always ask:

Are you 100% sure that your application will be web-only?

If the answer is not a resounding yes, a REST API starts to make a lot of sense.

Migrating from Inertia to API: what it actually involves

Migrating is not impossible, but it involves:

  • Creating endpoints from scratch
  • Reorganizing controllers
  • Adjusting business logic
  • Changing the frontend data flow

In medium or large projects, this change is not trivial.

When Inertia becomes an intermediate step

If you know there will be multiple platforms, Inertia is not the final destination, it's a transition. And every transition has a cost.

When to use Inertia and when a Rest API?

Another important point is deciding whether it is better to use Inertia or directly create a Rest API. My personal opinion is that it depends on the future of the application.

Use Inertia if…

  • If the application is going to migrate to another technology, such as a mobile or desktop application, it is recommended to create a Rest API.
  • If the application remains web-only, Inertia is sufficient and saves us a lot of work.
  • You want to move fast without extra complexity.
  • You don't plan for mobile apps or external integrations.

Use REST API if…

For example, in my Academy project, which is developed in Laravel with Vue, I opted to create a Rest API. This allowed me to later migrate the application to Flutter and saved me at least 40% of the work.

  • I already had the structure of the Rest API and could reuse the resources.
  • I could easily check the format of the queries through the Network tab in the browser.
  • It allowed me to quickly build new screens in Flutter, without the need to replicate the Vue logic with Inertia.
  • You need to expose data to third parties.
  • You prefer to invest more now to save later.

If I had used Inertia, I would have had more work:

  • Create the application in Vue with Inertia.
  • Create the Rest API to migrate to Flutter.
  • Replicate components and methods, generating redundancy and more maintenance.

That's why, in cases where migration or having multiple platforms is planned, I consider it better to create a Rest API from the start.

If, on the other hand, you don't plan to migrate the application and only want direct communication between Laravel and Vue, using Inertia is sufficient and avoids making unnecessary Axios requests.

Can Inertia and REST API be combined?

In what cases it makes sense:

Yes, and in fact, it is quite common:

  • Inertia for the web app
  • API for specific functionalities or external consumption

When you are complicating the project unnecessarily:

  • If you end up using the API for almost everything "just in case," you probably should have opted for an API from the start.

Conclusion: my honest recommendation after using both approaches

There is no universal answer.

But after working with Inertia and with REST APIs in real projects, my criteria are clear:

  • Inertia is excellent for closed web applications
  • The REST API is an investment in the future

In my case, for my project, betting on an API allowed me to evolve the project without redoing work. If I had chosen Inertia, I would have moved faster at the beginning... but I would have paid for that saving later. Especially given my developer profile—I like to take projects to mobile—usually Inertia is NOT for me since I believe that everything that can be seen in a web app can also be seen in a native app.

That's why, before deciding, don't just think about what you need today. Think about what you don't want to have to redo tomorrow.

Prerequisites and Installation

Before starting, make sure you have:

  • Laravel 9.x or higher
  • Node.js and npm installed.
  • A development environment with PHP 8+.

Laravel Mix or Vite

If you are using Laravel Mix in your Laravel project, these are the steps you need to follow; we are going to configure Vue 3 in a Laravel 9 project. First, let's install the dependencies:

Install Vue and dependencies

$ npm install --save vue@next
$ npm install vue-loader
  • Vue next refers to Vue 3.
  • Vue Loader is what allows Vue files to be translated into JavaScript.

As of Laravel 9.x, the framework uses Vite by default, but if you work with older versions, Laravel Mix is still valid.

Vue 3 is installed with vue@next, and vue-loader is responsible for translating .vue components into native JavaScript.

Configuration of the webpack.mix.js file (mix)

mix.js('resources/js/app.js', 'public/js')
.js('resources/js/vue/main.js', 'public/js')
.vue()
.postCss('resources/css/app.css', 'public/css', [
require('postcss-import'),
require('tailwindcss'),
require('autoprefixer'),
]);

Base project structure and mounting point

The next thing we are going to do is create a file with the Vue 3 instance, with which we can create components and more:

resources/js/vue/main.js:

import { createApp } from "vue";
import App from "./App.vue"
const app = createApp(App)
app.mount("#app")
Creamos el componente principal, el llamado App.vue:
resources/js/vue/App.vue:
<template>
    <div>
        <h1>Principal</h1>
        <list/>
    </div>
</template>
<script>
import List from "./componets/List";
export default {
    components:{
        List
    }
}
</script>

And use the @vite directive in Blade:

@vite('resources/js/vue/main.js')
<div id="app"></div>

And an example component, the list one:

resources/js/vue/components/List.vue:

<template>
  <div>
    <h1>Post List</h1>
  </div>
</template>

And with this, we have installed and configured Vue in a Laravel project; the next thing we are going to do is create a page from blade, to consume the Vue application:

Also note that we use the .vue function which allows the transpilation of .vue files into a js file; with this, the next thing we need is to create a page that we can use to display the Vue application:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Vue</title>
</head>
<body>    
    <div id="app"></div>
    <script src="{{ asset('js/main.js') }}"></script>
</body>
</html>
Y su ruta:
// web.php
Route::get('/vue', function () {
    return view('vue');
});

Add Vue 3 to the Laravel project (with Laravel Vite)

You have to follow these steps if you are developing a Laravel project that has the vite.config.js file.

We are going to add the Vue 3 dependencies to the Laravel project:

$ npm install --save vue@next
$ npm install vue-loader

vue@next is the current package to install vue 3, and vue-loader is simply the package that allows processing .vue files and generating an output file that the browser can understand. 

vue-loader is the loader for webpack that allows processing Vue components.

In addition to the previous packages, we have to install the Vue plugin with Vite:

$ npm install @vitejs/plugin-vue

And in the Vite configuration file, we add Vue:

vite.config.js

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue'
export default defineConfig({
    plugins: [
        vue(),
        laravel({
            input: [
                'resources/css/app.css',
                'resources/js/app.js',
            ],
            refresh: true,
        }),
    ],
});

Now, we have the dependencies ready, let's configure the project; for this, we are going to create the necessary files, which are basically the same as those that exist in a project generated with the Vue Cli.

We will create some files in the resources folder whose structure and functionality we indicate below.

This first file would be to build the main Vue instance; remember that it is also the one we use to configure any other plugin.

resources/js/vue/main.js:

import { createApp } from "vue";
import App from "./App.vue"
const app = createApp(App)
app.mount("#app")

With this, upon entering the mentioned route we have:

App in vue
App in vue

 

Modularizing components and best practices

Video thumbnail

Here comes the part that makes the difference. Integrating Vue is easy; making it scalable and reusable is what distinguishes a professional project.

Using props and dynamic routes

In my experience, modularizing components using props and configurable routes is key. This way, I could use the same payment component for different resources (courses, books, or packs) simply by changing the input parameters.
For example:

<PaymentPlugin 
 :slug="slug" 
 :price="price" 
 :redirect="redirectRoute" 
/>

Polymorphic relationships and reusable components

On the backend side, I leveraged Laravel's polymorphic relationships to manage different models with the same payment structure. This allowed me to maintain common business logic without duplicating code.

In the database, the same Payment model can belong to a course, a book, or a pack thanks to Laravel's morphTo relationships. This way, Vue only needs to know what type of resource it is handling, and the backend takes care of the rest.

Real example: adaptable payment component

This approach allowed me to build a reusable payment plugin that:

  • Validates if the user is authenticated.
  • Applies dynamic coupons.
  • Manages payments with PayPal (and soon Stripe).
  • Offers additional features like "gift course" or "view for free".
  • Each behavior is activated or deactivated according to the received props, without touching the component's base code.

Displaying Vue components from Blade

Laravel and Vue coexist perfectly. It's enough to create a Blade view that acts as an entry point:

// routes/web.php
Route::get('/vue', function () {
 return view('vue');
});

And inside resources/views/vue.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Vue App</title>
</head>
<body>
 <div id="app"></div>
 @vite('resources/js/vue/main.js')
</body>
</html>

Common errors when integrating Vue and how to solve them

  • ❌ The component does not mount: check that the div's id matches that of app.mount("#app").
  • ⚙️ Dependency problems: make sure to use vue@next for Vue 3.
  • ⚡ Hot reload does not work: verify that refresh: true is active in vite.config.js.
  • Confusion between Mix and Vite: use Vite for Laravel 9+ and Mix only if you maintain older projects.

The Best of Laravel - Its Native Integration with Node

The Best of Laravel - Its Native Integration with Node
Video thumbnail

One of the features I like most about Laravel is that it comes with Node.js integrated by default. This means that, in a single project, we can have:

  • An excellent server-side framework (Laravel).
  • Node.js, which is usually used on the client-side (frontend).

This is especially useful in projects with Laravel + Inertia, where we can see several key files:

  • package.json: Node.js files that are generated when creating any project with Node.
  • composer.json: PHP files managed by Laravel, which control backend dependencies.

If we compare it with other web frameworks, such as Django, Flask, FastAPI or CodeIgniter, they are all excellent, but they do not offer this native integration with Node.js.

Node and Laravel: the best combination

Suppose you want to create a complete project with backend and frontend:

  • In frameworks like Django or Flask, you would need to create two independent projects:
    • The backend for the administrative part.
    • A frontend project in Vue, React or similar, which consumes the backend API.

This implies maintaining two separate projects and communicating them using REST APIs or similar technologies.

With Laravel + Node.js, you can avoid this, since both coexist in a single project, facilitating development and integration.

CDN: option for testing

There is also the option of using the Vue or React CDN, but you would lose many advantages of the Node ecosystem, such as:

  • Dependency management with NPM or Yarn.
  • Asset compilation and optimization.
  • Use of modern frontend development tools.

Therefore, the CDN is usually only used for quick tests, not in serious projects.

Inertia.js: perfect integration

Laravel offers integrations like Inertia.js, which works as a wrapper to use Vue or React directly from Laravel. This allows:

  • Merging backend and frontend into a single project.
  • Avoiding the need to create two independent projects.
  • Keeping the backend logic in Laravel and working the frontend with Vue or React naturally.

In my experience, this integration has gained strength starting with Laravel 12, where when creating a project you are asked if you want to use:

  • A clean Laravel installation.
  • Laravel + React.
  • Laravel + Vue (with Inertia.js).

With Inertia.js, a perfect integration is achieved, although you can also opt for the traditional approach with REST API, where the backend and frontend remain separate projects but communicate through API endpoints.

Integration example

With Inertia.js: Laravel returns a Vue component directly from the server using Inertia::render().

In the classic approach: Laravel acts as an API, and Vue is consumed through calls to the endpoints defined in controllers grouped, for example, in an API folder.

This demonstrates Laravel's flexibility: you can choose between a complete integration in a single project or maintaining a separate backend and frontend based on your needs.

Conclusion and next steps

Integrating Vue 3 into Laravel is simpler than it seems, but doing it well means going beyond the basic tutorial.
In my case, modularizing each component and relying on Laravel's polymorphic relationships allowed me to scale my application without headaches, reusing code and maintaining project coherence.

If you have already managed to integrate Vue into your environment, the next step is to apply these best practices to maintain clean, reusable, and scalable code.

FAQs

  • Which is better: Laravel Mix or Vite for Vue?
    • Vite is the current standard since Laravel 9. It is faster and more modern than Mix.
  • Can I use Vue 2 in Laravel?
    • Yes, but it is recommended to migrate to Vue 3 for compatibility with Vite and long-term support.
  • How do I render a Vue component inside Blade?
    • You only need to create the div with id="app" and mount Vue onto it with createApp(App).mount("#app").

Frequently asked questions about Inertia and REST API in Laravel

  • Does Inertia replace a REST API?
    • No. Inertia avoids creating a full API, but it is not suitable for exposing data to other clients.
  • Does Inertia work for large applications?
    • Yes, as long as they are web applications and do not require multiple clients.
  • Is it a bad idea to start with Inertia?
    • No, as long as you are clear that it might not be the final approach.
  • Can I migrate to a REST API later?
    • Yes, but it involves extra work. The larger the project, the higher the cost.

The next step is to learn how to use custom error pages.

We're going to configure Vue 3 in a Laravel project using Node, and with this, take advantage of the entire Node ecosystem together with Laravel.


Únete a la comunidad de desarrolladores que han decidido dejar de picar código y empezar a construir productos reales. Recibe mis mejores trucos de arquitectura cada semana:

I agree to receive announcements of interest about this Blog.

Andrés Cruz

ES En español