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.


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:

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.


Laravel Newsletter

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


Version 4 of Tinkerwell is available now. Get the most popular PHP scratchpad with all its new features and simplify your development workflow today.

Visit Tinkerwell
Laravel Forge logo

Laravel Forge

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

Laravel Forge
Tinkerwell logo


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

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


Providing innovation and stability to ensure your web application succeeds.

Shift logo


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

Bacancy logo


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!

Lucky Media logo

Lucky Media

Bespoke software solutions built for your business. We ♥ Laravel

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


The official Laravel job board

Larafast: Laravel SaaS Starter Kit logo

Larafast: Laravel SaaS Starter Kit

Larafast is a Laravel SaaS Starter Kit with ready-to-go features for Payments, Auth, Admin, Blog, SEO, and beautiful themes. Available with Vue and Livewire stacks.

Larafast: Laravel SaaS Starter Kit
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a 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
Rector logo


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


The latest

View all →
Create a DateTime from a Timestamp With this New Method Coming to PHP 8.4 image

Create a DateTime from a Timestamp With this New Method Coming to PHP 8.4

Read article
Neovim Plugin to for Navigating Laravel and Livewire Components image

Neovim Plugin to for Navigating Laravel and Livewire Components

Read article
Laravel Herd v1.7 is out with updates to the dump UI image

Laravel Herd v1.7 is out with updates to the dump UI

Read article
Share Error Package for Laravel's New Exception Page image

Share Error Package for Laravel's New Exception Page

Read article
Sentry and Laravel announce a new partnership image

Sentry and Laravel announce a new partnership

Read article
"Fast Projects" by Larafast: Real life, ready to use Laravel based Boilerplates image

"Fast Projects" by Larafast: Real life, ready to use Laravel based Boilerplates

Read article