How To Install Vue 3 in Laravel 8 From Scratch - TechvBlogs

How To Install Vue 3 in Laravel 8 From Scratch

A step-by-step guide to installing, mounting, and displaying Vue 3 components in a base Laravel 8 install.

Suresh Ramani - Author - TechvBlogs
Suresh Ramani

8 months ago

What is Vue.js?

Vue is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only and is easy to pick up and integrate with other libraries or existing projects. Vue.js is a popular front-end framework for building Single Page Apps. It provides structure and abstraction. We can also create a server-side rendering application using Vue.js, It's helpful for SEO (Search Engine Optimization).

In older versions of Laravel, Vue was set up automatically with every new installation. However, in newer versions, you have to set it up yourself.

Luckily, the laravel/ui the package provides an easy way to set up Vue in your Laravel application.

A step-by-step guide to installing, mounting, and displaying Vue 3 components in a base Laravel 8 install.


  • Laravel 8
  • Laravel Mix 6
  • Node version >=12.14

Step 1: Install Laravel Project

First, open Terminal and run the following command to create a fresh laravel project:

#! /bin/bash
composer create-project --prefer-dist laravel/laravel laravel-vue3

or, if you have installed the Laravel Installer as a global composer dependency:

#! /bin/bash
laravel new laravel-vue3

Step 2: Install Vue & Dependencies

First, we'll want to install Vue 3 and our dev dependencies:

#! /bin/bash
npm install --save [email protected] && npm install --save-dev [email protected]

Step 3: Prepare Mix for Vue

Let's go to our webpack.mix.js file and add a .vue() method chain. Our mix file should now look something like this:

const mix = require('laravel-mix');

 | Mix Asset Management
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel applications. By default, we are compiling the CSS
 | file for the application as well as bundling up all the JS files.

mix.js('resources/js/app.js', 'public/js')
    .postCss('resources/css/app.css', 'public/css', [

Read Also: Laravel 8 Import Export Excel & CSV File Example

Step 4: Create your Vue 3 component

Next, let's go ahead and create our Vue 3 component. In the spirit of adventure, let's use the new composition API. Create a file /resources/js/components/Welcome.vue as so:

    <h1>{{ title }}</h1>
export default {
    setup: () => ({
        title: 'How To Install Vue 3 in Laravel 8 From Scratch'

Step 5: Import Vue to the Laravel javascript file

First, we are not going to import Vue, we are going to import a named export from Vue 3 called createApp.

import { createApp } from 'vue'

let's import our Welcome component and create the Vue app.

import HelloWorld from './components/Welcome.vue';

const app = createApp({});

Finally, our app.js the file will look something like this:


import { createApp } from 'vue'
import HelloWorld from './components/Welcome'

const app = createApp({})

app.component('hello-world', HelloWorld)


Step 6: Prepare Blade for Vue

Now let us create an element that has that id. To do this, we can remove the standard markup found in our welcome.blade.php file and replace it with this:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">


        <!-- Fonts -->
        <link href="[email protected];600;700&display=swap" rel="stylesheet">

        <!-- Styles -->
            /*! normalize.css v8.0.1 | MIT License | */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.dark\:text-gray-500{--tw-text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--tw-text-opacity))}}

            body {
                font-family: 'Nunito', sans-serif;
    <body class="antialiased">
        <div id="app">
            <div class="relative flex items-top justify-center min-h-screen bg-gray-100 sm:items-center py-4 sm:pt-0">

        <script src="{{ mix('js/app.js') }}"></script>

As you can see, we have a div with the id app that will be replaced by our Vue app.

Below we have a script tag that references not the app.js we have in resources/js but within our public/js folder. This file is the end result of our webpack build process.

Step 7: Compile Javascript Assets

Lastly, we'll run

#! /bin/bash
npm run watch

Run php artisan serve and check http://localhost:8000

Thank you for reading this blog.

Read Also: Build Crud API with Node.js, Express, and MongoDB

If you want to manage your VPS / VM Server without touching the command line go and  Checkout this linkServerAvatar allows you to quickly set up WordPress or Custom PHP websites on VPS / VM in a  matter of minutes.  You can host multiple websites on a single VPS / VM, configure SSL certificates, and monitor the health of your server without ever touching the command line interface.

If you have any queries or doubts about this topic please feel free to contact us. We will try to reach you.

Comments (2)

Evgeny Evgeny 1 week ago

ERROR in ./resources/js/app.js 4:0-46 Module not found: Error: Can't resolve './components/Welcome' in...

There is no such way in Laravel 8

Evgeny Evgeny 1 week ago

Missed one step. I'm sorry)

Multiple sites on a single VPS/VM/Server Deploy PHP sites on cloud in minutes, simple as that!
Generate Free SSL Certificate - SSLForWeb

Generate Free SSL Certificate

  • 90-Day Certificates
  • SSL Monitoring
  • Wildcard Certificates
  • Multiple Domains
  • Certificate Management
  • Expiration Reminders


Note: All Input Fields are required.