Building a Vue SPA with Laravel


January 4th, 2018

Building a Vue SPA with Laravel

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.


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:


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'
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 },

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:

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

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:

<p>This is the homepage</p>

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

<p>Hello World!</p>

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:

| 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:

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

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:

Vue Router Laravel Demo App

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!

Filed in:

Paul Redmond

Full stack web developer. Author of Lumen Programming Guide and Docker for PHP Developers.