Articles

Vue and Laravel: create a Single Page Application

Laravel is one of the most popular PHP frameworks used by developers, let's see today how to make a Single Page Application with VueJs.

Before the launch of Laravel UI, one of its main features was pre supportdefinite for Vue.js from Laravel v5.3 to v6. Vue is a modern JavaScript frontend framework used to build user interfaces.

Why install Laravel and Vue together?

Here are some of the main benefits of using Laravel with Vue to create a complete workflow for your projects:

The source code is combined into one project, instead of having separate projects for the backend and frontend
Installation and configuration are simple
A single distribution can manage both frameworks together

What is a SPA? (single page application)

Una single page application (SPA for short) dynamically loads new data from a web server into a web page without having to refresh the entire page.

Examples of popular websites that use SPAs include gmail.com and youtube.com – in other words, SPAs are largely ubiquitous. Most of the admin dashboards you might work with on a daily basis are built using SPA.

Advantages of SPAs:

The user experience is more flexible
Cache data in the browser
Fast loading time


Disadvantages of SPAs:

May compromise SEO (search engine optimization)
Potential security issues
It consumes a lot of browser resources

Project configuration in Laravel

This post will demonstrate how to develop a to-do app that allows users to sign up for an account and add tasks.

For this tutorial, Laravel 9 is used, which requires PHP 8.1 and Vue 3; we also need to have PHP and NGINX installed.

We start with the following command:

composer create-project --prefer-dist laravel/laravel laravel-vue-combo

Next, we'll install the JavaScript dependencies.

npm install

We need to install some packages before we can add Vue to our project.

Also, plugin-vue must be installed, since Laravel 9 ships with Vite, rather than webpack-mix, which was the previous Laravel bundler for JavaScript. Let's do it now:

npm install vue@next vue-loader@next @vitejs/plugin-vue

Open the file called vite.config.js and add vue() to configuration:

Innovation newsletter
Don't miss the most important news on innovation. Sign up to receive them by email.
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue'

export default defineConfig({
    plugins: [
        vue(),
        laravel([
            'resources/css/app.css',
            'resources/js/app.js',
        ]),
    ],
});

Edit the app.js file and bootstrap snippet for the Vue 3 app:

require('./bootstrap');

import {createApp} from 'vue'

import App from './App.vue'

createApp(App).mount("#app")

Create a file called App.vue and add the following:

<template>
  <h1> Hello, Vuejs with Laravel </h1>
</template>
<script>
export default {
  setup() {

   }
}
</script>

Finally, open the file welcome.blade.php located in the folder resources/views and add the following:

<!DOCTYPE html>
<html>
<head>
 ....
        @vite('resources/css/app.css')
</head>
<body>
  <div id="app"></div>
  @vite('resources/js/app.js')
</body>
</html>

To preview our app, we need to start our Vue app and Laravel server on two different terminals/command lines:

npm run dev


php artisan serve

To create our to-do app, we need to create other files. Vue will create multiple pages, mainly:

  • access
  • for the registration
  • A home page


To communicate with Laravel endpoints, we need to install Axios:

npm install axios

vue routing

Using the package vue-router, there are various routing strategies that can be used in Vue; these strategies are also known as history models.

When a user requests route like http://localhost:8000/home, which will return a 404 error when the page is refreshed, we can rely on Laravel to detect any fallback routes and then serve the Blade file which contains our app.

For this reason, we will use HTML5 mode:

Route::get('/{vue_capture?}', function() {
    return view('welcome');
})->where('vue_capture', '[\/\w\.-]*');
import {createRouter, createWebHistory} from 'vue-router';

const router = createRouter({
    history: createWebHistory(),
    routes: [
        {
            path: '/',
            component: () => import('./pages/Login.vue')
        },
        {
            path: '/register',
            component: () => import('./pages/Register.vue')
        },
        {
            path: '/home',
            component: () => import('./pages/Home.vue')
        }
    ],
})

In this example we handle authentication using Laravel Sanctum, then the token is saved in local storage.

For other requests to succeed, the token is merged into the header, which will allow the user making the request to be identified by Laravel.

Here are the associated code blocks for both:

<!--Login.vue-->
<template>
    <div class="mx-auto w-4/12 mt-10 bg-blue-200 p-4 rounded-lg">
        <div
            class="bg-white shadow-lg rounded-lg px-8 pt-6 pb-8 mb-2 flex flex-col"
        >
            <h1 class="text-gray-600 py-5 font-bold text-3xl"> Login </h1>
            <ul class="list-disc text-red-400" v-for="(value, index) in errors" :key="index" v-if="typeof errors === 'object'">
                <li>{{value[0]}}</li>
            </ul>
            <p class="list-disc text-red-400" v-if="typeof errors === 'string'">{{errors}}</p>
            <form method="post" @submit.prevent="handleLogin">
            <div class="mb-4">
                <label
                    class="block text-grey-darker text-sm font-bold mb-2"
                    for="username"
                >
                    Email Address
                </label>
                <input
                    class="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker"
                    id="username"
                    type="text"
                    v-model="form.email"
                    required
                />
            </div>
            <div class="mb-4">
                <label
                    class="block text-grey-darker text-sm font-bold mb-2"
                    for="password"
                >
                    Password
                </label>
                <input
                    class="shadow appearance-none border border-red rounded w-full py-2 px-3 text-grey-darker mb-3"
                    id="password"
                    type="password"
                    v-model="form.password"
                    required
                />
            </div>
            <div class="flex items-center justify-between">
                <button
                    class="bg-blue-500 hover:bg-blue-900 text-white font-bold py-2 px-4 rounded"
                    type="submit"
                >
                    Sign In
                </button>
                <router-link
                    class="inline-block align-baseline font-bold text-sm text-blue hover:text-blue-darker"
                    to="register"
                >
                    Sign Up
                </router-link>
            </div>
            </form>
        </div>
    </div>
</template>
export default {
    setup() {
        const errors = ref()
        const router = useRouter();
        const form = reactive({
            email: '',
            password: '',
        })
        const handleLogin = async () => {
            try {
                const result = await axios.post('/api/auth/login', form)
                if (result.status === 200 && result.data && result.data.token) {
                    localStorage.setItem('APP_DEMO_USER_TOKEN', result.data.token)
                    await router.push('home')
                }
            } catch (e) {
                if(e && e.response.data && e.response.data.errors) {
                    errors.value = Object.values(e.response.data.errors)
                } else {
                    errors.value = e.response.data.message || ""
                }
            }
        }

        return {
            form,
            errors,
            handleLogin,
        }
    }
}

Ercole Palmeri

You might also be interested in ...

Innovation newsletter
Don't miss the most important news on innovation. Sign up to receive them by email.

Latest Articles

Veeam features the most comprehensive support for ransomware, from protection to response and recovery

Coveware by Veeam will continue to provide cyber extortion incident response services. Coveware will offer forensics and remediation capabilities…

April 23 2024

Green and Digital Revolution: How Predictive Maintenance is Transforming the Oil & Gas Industry

Predictive maintenance is revolutionizing the oil & gas sector, with an innovative and proactive approach to plant management.…

April 22 2024

UK antitrust regulator raises BigTech alarm over GenAI

The UK CMA has issued a warning about Big Tech's behavior in the artificial intelligence market. There…

April 18 2024

Casa Green: energy revolution for a sustainable future in Italy

The "Green Houses" Decree, formulated by the European Union to enhance the energy efficiency of buildings, has concluded its legislative process with…

April 18 2024