DEV Community

Cover image for Set up Vue, Vuex, Vue-Router & Sass in Laravel 8
Martins Onuoha
Martins Onuoha

Posted on

Set up Vue, Vuex, Vue-Router & Sass in Laravel 8

Laravel’s scaffolding for both frontend and backend development has evolved in the past couple of version releases. For example, when scaffolding authentication in Laravel 4 & 5, we’d use the good old “make: auth” command, which was quite declarative and didn’t give any other frontend options besides Blade. This command pretty much scaffolds a basic authentication system, including routes, views, and controllers necessary for registration, login, and password recovery.

This is convenient, of course, if you intend to use Blade for your views and SSR, but sometimes that’s not the case. Say you intend to build a SPA with your favorite Javascript library but still want all the goodness of Laravel.

Until Laravel 5.3, Blade was the go-to option for fleshing out your frontend in Laravel. The support for Vue.js in 5.3 introduced the use of Vue components within your Laravel application. By default, Laravel generated a Example.vue component in the resources/assets/js/components directory. It was up to you to decide whether or not you wanted to use it.

Support for using React was introduced in version 5.5, by allowing you to use the Artisan preset command to remove or set what frontend scaffolding preset you prefer.
php artisan preset react or php artisan preset none

This flow was discontinued in later versions, precisely version 6.x.

Page not found

The replacement?

In Laravel 6.x, the laravel/ui package was introduced to make scaffolding Javascript frontend apps in Laravel easier, with Vue, React & Bootstrap support. It’s pretty straightforward to set up (although this isn’t the route we’d be taking.). You can check the official documentation to learn about scaffolding with the Laravel/UI package.

This flow was also discontinued in version 8. However, it is backward compatible, so even though you’re on version 8 of Laravel, you can still scaffold a vue app using the Laravel/UI package.

The replacement?


Laravel Starter Kits

These are the currently supported scaffolding options…if they don’t change with the next major release. Here, there are two options for scaffolding:

With Laravel Breeze, the Laravel team isolated the popular “make: auth” command into a separate package, only this time, the generated Blade templates are styled with Tailwind CSS as opposed to Bootstrap, which was formerly used. Since we aren’t dealing with Blade, this option doesn’t apply now.

The second option, Laravel Jetstream, provides us with two options for scaffolding — Livewire + Blade or Inertia + Vue. No, we’re not going the Jetstream route either; why?

It’s bloated.

What we want is a Laravel + Vue setup at its barest minimum, something like what you get when you scaffold a vue application with the vue CLI.

Now we know the options we have, what we want, and why we want it. Let’s get started.


Getting Started

First, be sure your Laravel cli is up to date. For some reason, this error comes up for outdated versions.

Outdated version

Update your Laravel version with:

composer global require laravel/installer
Enter fullscreen mode Exit fullscreen mode

Updated version

Next, scaffold a Laravel application:

laravel new laravel-vue-minimal
Enter fullscreen mode Exit fullscreen mode

Laravel

Once that’s done, open up the project in a code editor, open the package.json file, find and replace every instance of npm with yarn. You can ignore this step if you want to keep using npm. We’re only doing this so we can install packages with yarn instead of NPM, a decision that’s mostly based on preference.

JSONPackage file

Your script object should look like this (Some unedited parts are omitted):

"script": {
    "dev": "yarn run development",
    "watch": "yarn run development --watch",
    "watch-poll": "yarn run watch --watch-poll",
    "prod": "yarn run production",
}
Enter fullscreen mode Exit fullscreen mode

If left unchanged, it should have npm commands only:

"scripts": {
    "dev": "npm run development",
    "watch": "npm run development --watch",
    "watch-poll": "npm run watch --watch-poll",
    "prod": "npm run production",
}
Enter fullscreen mode Exit fullscreen mode

Next, we’ll install the packages by typing the yarn command in the terminal:

yarn
Enter fullscreen mode Exit fullscreen mode

Or using npm

npm i
Enter fullscreen mode Exit fullscreen mode

We also need to install some required packages as dev dependencies.

yarn add -D vue vue-template-compiler vue-router vuex sass sass-loader
Enter fullscreen mode Exit fullscreen mode

or using npm

npm install --save-dev vue vue-template-compiler vue-router vuex sass sass-loader
Enter fullscreen mode Exit fullscreen mode

Vue Setup

Let’s create a basic Vue folder structure, which would live inside the resources/js folder. Our folder structure would look something like this:

js/
  - Components
  - Mixins
  - Pages
  - Router
  - Store
  - Styles
  App.vue
  app.js
  bootstrap.js
Enter fullscreen mode Exit fullscreen mode

You should have something like this now.

Folder

Next, we’ll set up Vue-Router.

Within the Router folder, create two files, “routes.js” and “index.js”. In the routes.js file, we’ll set up and export an array of routes.

const routes = [
  {
    path: '',
    component: () => import('../Pages/Home.vue'),
    name: 'home'
  },
  {
    path: 'about',
    component: () => import('../Pages/About.vue'),
    name: 'about'
  },
]

export default routes;
Enter fullscreen mode Exit fullscreen mode

Here we’re defining the home and about routes and mapping them to their respective components -  Home.vue and About.vue (which we’ll create in a bit). Next, We’ll set up a Vue Router instance in the index.js file (inside the Router folder) and export the instance.

import VueRouter from 'vue-router';
import routes from './routes';

const router = new VueRouter({
  mode: 'history',
  routes
})

export default router;
Enter fullscreen mode Exit fullscreen mode

Up next, Vuex.

We’ll set up a Vuex store, add a “count” state property, and one mutation that we’ll commit later to update the count property. Within the Store folder, create an index.js file and set the Vuex store up like so:

import Vue from 'vue';
import Vuex from 'vuex'

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    INCREMENT(state) {
      state.count++
    },
  },
  actions: {}
})

export default store;
Enter fullscreen mode Exit fullscreen mode

If you’ve worked with Vuex before, this should look familiar. If not, you can check the documentation.


We can now set up our entry file, equivalent to the main.js file you get when you generate a vue application with vue-cli. We’ll find this entry file in ./resources/js/app.js. Here we should find an almost empty file with bootstrap.js import at the top. Leave the import as-is, and we’ll set up our Vue instance below it. Import the Vuex store and vue router instance into the app.js entry file, then pass it into the Vue instance.

require('./bootstrap');

import Vue from 'vue'
import VueRouter from 'vue-router';

import router from './Router/index'
import store from './Store/index';
import App from './App.vue'

Vue.use(VueRouter)

const app = new Vue({
  el: '#app',
  router,
  store,
  components: { App }
});
Enter fullscreen mode Exit fullscreen mode

At this point, we can set up all the components required. The App.vue is the entry component like you might have seen in our vue instance setup. Because of that, we’ll have the router-view component in here.

<template>
  <div class="app">
    <Navbar></Navbar>
    <router-view></router-view>
  </div>
</template>

<script>
import './Styles/app.scss'

export default {
  name: 'app',
  components: {
    Navbar: () => import('./Components/Navbar.vue')
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Add a Navbar.vue component in the js/Components directory since we already imported it in the App.vue component. It’ll look something like this:

<template>
  <nav class="navBar">
    <ul class="navBar__link">
      <li><router-link :to="{ name: 'home' }">Home</router-link></li>
      <li><router-link :to="{ name: 'about' }">About</router-link> </li>
    </ul>
  </nav>
</template>

<script>
export default {
  name: 'navbar'
}
</script>
Enter fullscreen mode Exit fullscreen mode

We’ll style this later.

In the Pages folder, we’ll add the About.vue component and the Home.vue component, respectively. (We already used them in our route.js file).

About.vue

<template>
  <div class="about">
    <h2>
      About Page
    </h2>
  </div>
</template>

<script>
export default {
  name: 'about'
}
</script>
Enter fullscreen mode Exit fullscreen mode

Home.vue

<template>
  <div class="home">
    <img class="logo" width="100" src="logo.png" alt="">
    <h1>
      {{ count }}
    </h1>
    <button class="btn" @click="$store.commit('INCREMENT')">INCREMENT</button>
  </div>
</template>

<script>
import { mapState } from 'vuex';

export default {
  name: 'home',
  computed: {
    ...mapState({
      count: state => state.count
    }),
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

In the Home component, we’re using the mapState helper to get the count value from our state object. Then we’re committing the “INCREMENT” mutation when the button is clicked. We’re also showing the count value within the h1 tag.

Next, we’ll style our components. Create the app.scss file within the Styles directory (We already imported this in the App.vue file).

Note: We’re mostly doing this for aesthetics, the point of this walkthrough is in the setting up of Vue from ground up in Laravel, so feel free to style your components however you want.

We add some styling to the body:

$font-color: #555555;

body, html, #app, .app {
  padding: 0;
  margin: 0;
  background-color: #e6e6e6;
  text-align: center;
}
Enter fullscreen mode Exit fullscreen mode

Below that, we style the h1 tag and navBar class.

h1 {
  font-size: 10em;
  margin-top: 0;
}
.navBar {
  padding: 5px;
  background-color: #FFF;
  &__link {
    display: flex;
    margin: 0;
    li {
      margin: 10px;
      list-style-type: none;
      font-size: 17px;
      a {
        text-decoration: none;
        color: #555555;
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

And Finally, we style the button:

button {
  padding: 10px;
  background-color: #FFF;
  border: 1px solid $font-color;
  color: $font-color;
  cursor: pointer;
  outline: none;
  width: 200px;
}
button:hover {
  background-color: #000;
  color: #FFF;
}
Enter fullscreen mode Exit fullscreen mode

The entire app.scss file should look like this:

$font-color: #555555;

body, html, #app, .app {
  padding: 0;
  margin: 0;
  background-color: #e6e6e6;
  text-align: center;
}
h1 {
  font-size: 10em;
  margin-top: 0;
}
.navBar {
  padding: 5px;
  background-color: #FFF;
  &__link {
    display: flex;
    margin: 0;
    li {
      margin: 10px;
      list-style-type: none;
      font-size: 17px;
      a {
        text-decoration: none;
        color: #555555;
      }
    }
  }
}
button {
  padding: 10px;
  background-color: #FFF;
  border: 1px solid $font-color;
  color: $font-color;
  cursor: pointer;
  outline: none;
  width: 200px;
}
button:hover {
  background-color: #000;
  color: #FFF;
}
Enter fullscreen mode Exit fullscreen mode

Our work here is done. Now we have to make sure Laravel is conscious of our vue setup.


Laravel Setup

We’ll update the welcome.blade.php file within resources/views. This is the entry file that Laravel automatically creates when you scaffold a new Laravel project. Delete all you have there and Update the content to this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="shortcut icon" href="logo.png" type="image/x-icon">
    </script>
    <link rel="stylesheet" href="{{ mix('css/app.css') }}" />

    <title>{{env('APP_NAME')}}</title>
</head>
<body>
    <div id="app">
        <app></app>
    </div>

    <script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Finally, we’ll update the web.php file within the routes folder and set a catch-all route. This is similar to your .htaccess rules when you deploy a Vue app to a static host. The catch-all route should look like this:

<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/

Route::get('/{any?}', function() {
    return view('welcome');
});
Enter fullscreen mode Exit fullscreen mode

That’s it.


From here, you should run Laravel-mix to build the vue app and start your Laravel server.

yarn run watch
Enter fullscreen mode Exit fullscreen mode

Start the Laravel server:

php artisan serve
Enter fullscreen mode Exit fullscreen mode

Countdown

If everything goes right, you should be able to view the vue (no pun intended) app running on port (8000).

Too Lazy to go through the entire setup yourself?

Check out the Laravel-Vue-Minimal repo. It’s a minimal scaffolding for Vue and Laravel and pretty much everything in this walkthrough.

GitHub logo MartinsOnuoha / laravel-vue-minimal

A basic Vue Setup on Laravel 8. Including Sass, Vue-Router & Vuex.

Laravel-Vue-Minimal

Get started quickly with Vue, Vuex, Vue-Router in Laravel.

Setup

Clone the repository

git clone https://github.com/MartinsOnuoha/laravel-vue-minimal.git
Enter fullscreen mode Exit fullscreen mode

Install Dependencies

yarn && composer install
Enter fullscreen mode Exit fullscreen mode

Copy .env

cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

Generate App Key

php artisan key:generate
Enter fullscreen mode Exit fullscreen mode

Start Server

php artisan serve
Enter fullscreen mode Exit fullscreen mode
yarn run watch
Enter fullscreen mode Exit fullscreen mode

Project Structure

Vue

You can find the Vue app structure under resources/js

📁 Components
📁 Mixins
📁 Pages
📁 Router
📁 Store
📁 Styles
  🗳 App.vue
  🗳 App.js

Docker

This project uses Sail to run the app within a docker environment. If you would like to run the project within a docker container you can do this by first building the container and then running it.

Within the project directory run:

docker-compose build
Enter fullscreen mode Exit fullscreen mode

After the build is complete you can start the container by running:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

Alternatively if you have composer installed correclty and the composer command is available, then you can run:

composer run-script sail
Enter fullscreen mode Exit fullscreen mode

You…


Cheers ☕️

Top comments (0)