
Sun Oct 15 2023
8 min read
Vue into the Laravel Verse
In this story, I'll show my journey about add Vue to Laravel and how to set up Vue Router. And I'll explain how things work behind the scene as I go.
Laravel
Laravel is a powerful backend framework that offers a wide array of features necessary for building modern web applications and APIs. These features include routing, validation, caching, queues, file storage, database migrations, and much more.
Laravel blades
Laravel by default uses blades templating language, that provide short syntax for displaying data,
iterating over data, conditional statements, authentication directives, and many other features. Blade template files
use the .blade.php file extension and they are complied into plain PHP in the end.
Vue
In the Vue main page you will find this big title The Progressive JavaScript Framework, but what is a progressive framework?
Vue.js allows developers to adopt its features gradually. You can start by adding Vue to a small part of your project or an individual component without the need to rewrite your entire application (if it's already written in blades or something else). This flexibility is particularly useful when you want to modernize or improve the interactivity of existing web applications.
Vue with laravel
So why we would use Vue with laravel instead of creating a separate frontend project? Here are a few reasons:
- Create a full-stack workflow.
- The source code is combined into one project, rather than having separate projects for the backend and frontend.
- Pass props from your blade files into your Vue components.
- Dseploy both frameworks together in a single run.
Getting Started
Create a new laravel project
> composer create-project laravel/laravel laravel-vue
> cd laravel-vue
# install the javascript libraries and dependencies needed for our project
> npm install
# run the backend server
> php artisan serve
# run the frontend server, watch changes in your js assets
> npm run dev
Your server is now up and running at http://127.0.0.1:8000/.
Install vue.js
# The Sass package is optional; you can install it if needed.
> npm install --save-dev vue sass
Configure vite
Note: Vite is installed by default with new versions of Laravel, If you have an old existing project it may
have Webpack instead, so double-check your package.json for Vite first.
Vite used to bundle your application's CSS, HTML and Javascript into a production ready assets. It could be used with multiple frameworks, not just Vue, even with apps built without any framework.
Vite's plugins are used to extend the functionality of Vite and integrate it with many other tools and frameworks, including Vue.
First, Let's install the Vite's Vue plugin:
> npm install --save-dev @vitejs/plugin-vue
Let's change vite.config.js to add the Vue plugin.
// vite.config.js
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
// ... laravel plugin and other existing plugins here ...
vue({}),
],
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js',
},
},
});
For more advanced configurations, refer to the Vite Vue plugin documentation.
Configure webpack (optional)
If you have old Laravel project, you won't find Vite installed, another popular bundling package
called Webpack was used instead. So we need to add the configure vue inside webpack.mix.js instead
of vite.config.js.
const mix = require('laravel-mix');
mix
.js('resources/js/app.js', 'public/js')
.vue() // <- Add this
.postCss('resources/css/app.css', 'public/css', [
// ...
]);
Ok, we're now done with all installations and configurations.
Create a vue app instance
Now, let's modify the /resources/app.js file to create our Vue app instance, and register your components to it. Any
components registered here will be avaiable globally to use anywhere in your Vue app.
import './bootstrap';
import { createApp } from 'vue';
import VueApp from './components/VueApp.vue';
const app = createApp({});
app.component('vue-app', VueApp);
app.mount('#app');
If you opened your app now, you will see this message in the console
# [Vue warn]: Failed to mounst app: mount target selector "#app" returned null.
This is Vue trying to find the DOM element with id app where the Vue instance should be mounted. This is the root of
your Vue app, so it's up to you where to put this element. But for a simple case I put inside
the root view of my app, which is welcome.blade.php in my case.
Laravel routing
We define routes in laravel inside the /routes/web.php file for routes related to web-based applications and HTML
views or inside /routes/api.php file for routes related to RESTFUL APIs or any other non-web statless services.
My web.php routing file maps the root route / to welcome.blade.php, so there's where I'll define my #app vue
root element.
// /routes/web.php
Route::get('/', function () {
return view('welcome');
});
<!-- resources/views/welcome.blade.php -->
<div id="app">
<vue-app></vue-app>
</div>
Your first vue component
In Laravel, we create new Vue components in /resources/js/components directory to serve as the root component for our
vue app.
<!-- /resources/components/VueApp.vue-->
<template>
<h1>Welcome to my vue app</h1>
</template>
<script>
export default {
name: 'VueApp',
};
</script>
Vue and Sass
If you installed sass package in the first step, you need to add an entry point for vite to know how to compile and
bundle your SASS assets. Create a new file at resources/sass/app.scss.
Here's an example of the content of this file:
// resources/sass/app.scss
// Fonts
@import url('https://fonts.bunny.net/css?family=Nunito');
Passing data from laravel to vue
We also have the flexibility to pass down props from a laravel blade view to a vue component; this could be anything including a model variable. In this example, we pass the authenticated user to our root vue component.
<!-- welcome.blade.php -->
<vue-app :user-id="{{ Auth::user()->id }}"></vue-app>
<!-- /resources/components/VueApp.vue-->
<template>
<h1>Welcome to my vue app {{ userId }}</h1>
</template>
<script>
export default {
props: ['userId'],
};
</script>
Run the frontend server
It's the time to run the dev server to compile your Javascript and CSS assets. Vite will automatically watch for any
file changes and hot reload the app, no additional configurations needed.
> npm run dev
Loading your scripts and styles
We use the @vite() blade directive in the <head> of your application's root template with the entry points of
your js, css or scss, located at app.blade.phpfile, this directive locates and loads the compiled js
and scss files compiled to public/js/app.js.
<head>
<!-- ... your existing <head> and meta-tags here ... -->
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
</head>
If you don't use scss or only are using them inside your Vue components, you only need to include the JavaScript entry
point.
<head>
<!-- ... your existing <head> and meta-tags here ... -->
@vite('resources/js/app.js')
</head>
Again, if you're using webpack instead of vite, put this line at the end of you application's root template, mine
is welcome.blade.php.
<script src="{{ mix('/js/app.js') }}"></script>
Vue router
Ok, now, we have a Vue app up and running. But there's something important missing. What if the user wants to navigate to another page? We can use Laravel routing for this, but it would require sending requests to the server, waiting for it render the page, send us the response HTML back and reload the current page.
Our goal in this story is to build a Single Page App (SPA) where a user would only have to load the app once, then communicate with the backend using APIs to fetch new data. To do so we need to install Vue Router, a tool that allows us to navigate SPA Frontend applications.
Install vue router
> npm install vue-router
Setup vue router and routes
First, create two new components inside the /resources/js/components directory namedPage1.vue and Page2.vue.
Next, Let's edit the app.js file once again to configure vue router and put in some new routes.
// resources/js/app.js
import './bootstrap';
import { createApp } from 'vue';
import * as VueRouter from 'vue-router';
import VueApp from './components/VueApp.vue';
import Page1 from './components/Page1.vue';
import Page2 from './components/Page2.vue';
const routes = [
{ path: '/page1', component: Page1, name: 'page1' },
{ path: '/page2', component: Page2, name: 'page2' },
];
const router = VueRouter.createRouter({
history: VueRouter.createWebHashHistory(),
routes,
});
const app = createApp({});
app.component('vue-app', VueApp);
app.use(router);
app.mount('#app');
What we did here is installing vue router plugin to vue, to extend vue by adding routing functionality.
Next, edit your parent vue componentVueApp.vue file once again, to add the navigation links, and the vue router outlet
component.
<!-- /resources/js/components/VueApp.vue -->
<nav class="navbar">
<div class="container">
<ul class="navbar-nav">
<router-link :to="{ name: 'page1' }" class="nav-link">Page 1</router-link>
<router-link :to="{ name: 'page2' }" class="nav-link">Page 2</router-link>
</ul>
</div>
</nav>
<!-- router pages will be injected and switched here ... -->
<router-view></router-view>
How vue router works
VueApp: is our root vue component, the parent container, the whole vue app will be rendered inside this container.- What vue router is doing behind the scene is mapping some routes (urls) to some vue components. Here is maps
/pages1toPage1component for example. and when the URL changes, vue router then switch between these components in place of<router-view>outlet. - When navigating inside a Frontend app we use the
<router-linkand specify either the path or the component name we want to navigate to. We use it instead of<a href="...">This allows Vue Router to change the URL without reloading the page.
More about vue rotuer and how to use it for advanced cases here
Source Code
The source code to the application in this story is available on Github