Building a Vue SPA with Laravel

Published on by

Building a Vue SPA with Laravel image

Building a Vue single page application (SPA) with Laravel is a beautiful combination for building clean API-driven applications. In this tutorial, we show you how to get up and running with Vue router and a Laravel backend for building a SPA. We will focus on the wiring up all the pieces needed, and then in a follow-up tutorial, we will further demonstrate using Laravel as the API layer.

The primary flow of how an Vue SPA works with Laravel as a backend is as follows:

  • The first request hits the server-side Laravel router
  • Laravel renders the SPA layout
  • Subsequent requests leverage the history.pushState API for URL navigation without a page reload

Vue router can be configured to use history mode or the default hash-mode, which uses the URL hash to simulate a full URL so the page won’t reload when the URL changes.

We will use history mode, which means we need to configure a Laravel route that will match all possible URLs depending on which route the user enters the Vue SPA. For example, if the user refreshes a /hello route, we’ll need to match that route and return the Vue SPA application template. The Vue Router will then determine the route and render the appropriate component.

Installation

To get started, we will create a new Laravel project and then install the Vue router NPM package:

laravel new vue-router
cd vue-router
 
# Link the project if you use Valet
valet link
 
# Install NPM dependencies and add vue-router
yarn install
yarn add vue-router # or npm install vue-router

We have a Laravel installation and the vue-router NPM package ready to go. Next, we’ll configure the router and define a couple of routes and components.

Configuring Vue Router

The way that Vue Router works is that it maps a route to a Vue component and then renders it within this tag in the application:

<router-view></router-view>

The router view is within the context of the Vue application component that surrounds the entire application. We will come back to the App component momentarily.

First, we are going to update the main JavaScript file resources/assets/js/app.js and configure Vue router. Replace the contents of the app.js file with the following:

import Vue from 'vue'
import VueRouter from 'vue-router'
 
Vue.use(VueRouter)
 
import App from './views/App'
import Hello from './views/Hello'
import Home from './views/Home'
 
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/hello',
name: 'hello',
component: Hello,
},
],
});
 
const app = new Vue({
el: '#app',
components: { App },
router,
});

We have a few files that we need to create, but first, let’s cover the contents of app.js:

  • We import and install the VueRouter plugin with Vue.use()
  • We import three Vue components:
    • an App component that is the outermost application component,
    • A Hello component that maps to the /hello route
    • A Home component that maps to the / route
  • We construct a new VueRouter instance that takes a configuration object
  • We make Vue aware of the App component by passing it to the components property in the Vue constructor
  • We inject the router constant into the new Vue application to get access to this.$router and this.$route

The VueRouter constructor takes an array of routes, where we define the path, the name (just like Laravel’s named route), and the component that maps to the path.

I like to move my route definitions into a separate routes module that I import, but for the sake of simplicity we’ll define the routes within the main application file.

In order for Laravel mix to run successfully, we need to define the three components:

mkdir resources/assets/js/views
touch resources/assets/js/views/App.vue
touch resources/assets/js/views/Home.vue
touch resources/assets/js/views/Hello.vue

First, the App.vue file is the outermost container element for our application. Inside this component, we’ll define an application heading and some navigation using Vue Router’s <router-link/> tag:

<template>
<div>
<h1>Vue Router Demo App</h1>
 
<p>
<router-link :to="{ name: 'home' }">Home</router-link> |
<router-link :to="{ name: 'hello' }">Hello World</router-link>
</p>
 
<div class="container">
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {}
</script>

The most important tag in our App component is the <router-view></router-view> tag, which is where our router will render the given component that matches the route (i.e. Home or Hello).

The next component we need to define is located at resources/assets/js/views/Home.vue:

<template>
<p>This is the homepage</p>
</template>

Lastly, we define the Hello component located at resources/assets/js/views/Hello.vue:

<template>
<p>Hello World!</p>
</template>

I like separating my reusable components from my view-specific components by organizing my views into the resources/assets/js/views folder and my truly reusable components in resources/assets/js/components. This is my convention, and I find it works well so I can easily separate which components are intended to be reusable and which components are view-specific.

We have everything we need to run our Vue application as far as the frontend is concerned! Next, we need to define the backend route and the server-side template.

The Server-Side

We leverage an application framework like Laravel with a Vue SPA so that we can build a server-side API to work with our application. We can also use Blade to render our application and expose environment configuration through a global JavaScript object, which is convenient in my opinion.

In this tutorial, we aren’t going to build out an API, but we will in a follow-up. This post is all about wiring up the Vue router.

The first thing we’ll tackle on the server-side is defining the route. Open the routes/web.php file and replace the welcome route with the following:

<?php
 
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
 
Route::get('/{any}', 'SpaController@index')->where('any', '.*');

We define a catch-all route to the SpaController which means that any web route will map to our SPA. If we didn’t do this, and the user made a request to /hello, Laravel would respond with a 404.

Next, we need to create the SpaController and define the view:

php artisan make:controller SpaController

Open the SpaController and enter the following:

<?php
 
namespace App\Http\Controllers;
 
use Illuminate\Http\Request;
 
class SpaController extends Controller
{
public function index()
{
return view('spa');
}
}

Lastly, enter the following in resources/views/spa.blade.php:

 
<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 SPA Demo</title>
</head>
<body>
<div id="app">
<app></app>
</div>
 
<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>

We’ve defined the required #app element which contains the App component that Vue will render, along with rendering the appropriate component based on the URL.

Running the Application

The foundation is in place for building an SPA with Vue and Vue Router. We need to build or JavaScript to test it out:

yarn watch # or npm run watch

If you load up the application in your browser you should see something like the following:

Going Forward

We have the skeleton for a Vue SPA that we can start building using Laravel as the API layer. This app still has much to be desired that we will cover in a follow-up tutorial:

  • Define a catch-all 404 route on the frontend
  • Using route parameters
  • Child routes
  • Making an API request from a component to Laravel
  • Probably much more that I’m not going to list here…

The goal of this tutorial was the lay the groundwork for showing you how easy you can start an SPA with Vue Router. If you’re not familiar with it, check out the Vue Router documentation

Now, onward to Part 2!

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo

LaraJobs

The official Laravel job board

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate logo

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate

Build your SaaS application in hours. Out-of-the-box multi-tenancy and seamless Stripe integration. Supports subscriptions and one-time purchases, allowing you to focus on building and creating without repetitive setup tasks.

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector
MongoDB logo

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB

The latest

View all →
Bluesky notification channel for Laravel image

Bluesky notification channel for Laravel

Read article
Sprout Multitenancy Package for Laravel image

Sprout Multitenancy Package for Laravel

Read article
Creating a CLI Application With Laravel and Docker image

Creating a CLI Application With Laravel and Docker

Read article
Laravel Herd Adds Native MongoDB Support image

Laravel Herd Adds Native MongoDB Support

Read article
How to Create Tech Videos for YouTube with Josh Cirre image

How to Create Tech Videos for YouTube with Josh Cirre

Read article
Integrate Unsplash in Your Laravel Application image

Integrate Unsplash in Your Laravel Application

Read article