First Vue.js "Hello World" with Laravel & Vite

Let's install a new Laravel project and add some static text to the welcome page. But that static text would come from the Vue.js component, and not from the Blade.



Step 1/4. Create Laravel HTML/Blade Project 

In the Terminal, we run:

laravel new project


I'm using Laravel Valet which prepares a subdomain for me, so this is what I see as the homepage:



Let's go to resources/views/welcome.blade.php and add some static HTML like <h1> tag:

<div class="flex justify-center pt-8 sm:justify-start sm:pt-0">    
    <svg viewBox="0 0 651 192" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-16 w-auto text-gray-700 sm:h-20">        
        <g clip-path="url(#clip0)" fill="#EF3B2D">        
        <!-- ... official Laravel logo -->        
        </g>    
    </svg>
</div>

<h1>Hello World from Blade</h1>

<div>
<!-- ... other text -->
</div>


This is how it looks now:


So, the goal of our lesson is to replace that <h1> with a Vue.js component.




Step 2/4. Create the Vue.js Component


We create a file resources/js/components/HelloWorld.vue, with this code inside:

<template>    
    <h1>Hello World from Vue</h1>
</template>


Every Vue.js component has two sections: <template> for the HTML part, and then <script> for all the dynamic behavior.
For this static text component, we don't even need the <script> section, but we'll get to the appropriate full example in a minute.


Then, in our resources/views/welcome.blade.php file we change the old <h1> to this code - <div id="app"></div>:

<div class="flex justify-center pt-8 sm:justify-start sm:pt-0">    
    <svg viewBox="0 0 651 192" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-16 w-auto text-gray-700 sm:h-20">        
        <g clip-path="url(#clip0)" fill="#EF3B2D">        
        <!-- ... official Laravel logo -->        
        </g>    
    </svg>
</div>

<div id="app"></div>

<div>
<!-- ... other text -->
</div>


See this <div id="app"></div>?
This is exactly the place, where our Vue.js application will live. So, the whole project template is Blade, but inside of that, you can choose any internal HTML tag to be dynamic and contain Vue. 

For that, you assign id="whatever-name" to it, and then you will mount the Vue.js application to specifically that tag id. We'll get to that in a few minutes.




Step 3/4. Install Vue and configure Vite 


Now, let's pay attention to how our future Vue.js components will be built after we write their code. 

This process changed from Laravel v9.19 when they changed the default front-end asset bundler tool from Laravel Mix to Vite.
From July 2022, we need to configure Vite in the file vite.config.js, which is in the main Laravel folder and partly already pre-filled for us. Before that, you would need to work with webpack.mix.js file.

To enable Vite so it would work with our main resources/js/app.js and resources/css/app.css files, in the resources/views/welcome.blade.php we add this Blade directive before </head>:

    @vite(['resources/js/app.js', 'resources/css/app.css'])
</head>


It instructs the application to run Vite and build those two JS and CSS files to use in the browser.

Vite itself doesn't know anything about Vue.js or any other JS framework, the default vite.config.js is only about Laravel:

import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'

export default defineConfig({    
    plugins: [        
        laravel([            
            'resources/css/app.css',            
            'resources/js/app.js',
        ]),
    ],
});


If you open the file in the IDE like PhpStorm, you may notice that 'vite' and 'laravel-vite-plugin' are underlined, as not installed yet.

But they are listed among the front-end packages in the main package.json file:

{
    "private": true,
    "scripts": {
        "dev": "vite",
        "build": "vite build"
    },
    "devDependencies": {
        "axios": "^0.25",
        "laravel-vite-plugin": "^0.2.1",
        "lodash": "^4.17.19",
        "postcss": "^8.1.14",
        "vite": "^2.9.11"
    }
}


So to install them, we run:

npm install


Now, they shouldn't be underlined anymore, and let's add Vue.js to them.


We install Vue itself:

npm i vue@next

Here, you may specify a specific version or just @next which will install Vue 3, in my case.


Also, for Vue to work with Vite, we need to install a specific plugin:

npm i @vitejs/[email protected]

Notice that we're installing specific version 2.3.3 here which is compatible with the Vite 2 version which is the active version in Laravel, at the time of writing this article.

And now, we add both of those into the vite.config.js:

import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'

export default defineConfig({    
    plugins: [        
        laravel([            
            'resources/css/app.css',            
            'resources/js/app.js',
        ]),
        vue({
            template: {
                transformAssetUrls: {
                   base: null,
                   includeAbsolute: false,
                },
            },
        })
    ],
});


We add import vue and vue() with parameters. Those parameters are optional, I've just looked at how Laravel starter kit Laravel Breeze configures Vite.




Step 4/4. Load Vue.js Component into Blade


So, at this point:

- We have a Vue component
- We have a place id="app" to load it to
- We have installed Vue and configured Vite


The final step is actually to create a Vue.js application, or so-called "root component", and load it into the main resources/js/app.js file.

We create the file of the main "Root Component" - resources/js/App.vue

<template>
    <HelloWorld />
</template>

<script setup>
import HelloWorld from "./components/HelloWorld.vue"
</script>


As you can see, we use <script> here, and not only <template>. In general, the logic is this:

1. In the <script> section, we import what we need and create all the dynamic Vue.js stuff

2. Then, we can use all of that in the <template>, just like we're doing with the <HelloWorld /> component. 


Finally, we tie it all together by creating a Vue application in the resources/js/app.js file. At the bottom of the import './bootstrap' line, we add these:

import { createApp } from "vue"
import App from "./App.vue"
createApp(App).mount('#app')


I think it's all self-explanatory, but I will reiterate: we create a Vue application with root component App.vue, and mount it to the <div id="app"> from the Blade file.

Now, we need to start the Vite server, by running:

npm run dev


Here's how it looks in the Terminal:


And that's it, Vite server will stay running, waiting for you to make changes in the Vue files.

Here's what the browser will show:






Optional Step: Vite - Load CSS from JS


I see that some developers prefer to import the CSS into the JS file, to not reference it twice. So, you may make such changes.

In vite.config.js - remove CSS, and leave only JS:

plugins: [
    laravel([
        'resources/js/app.js',
    ]),


In resources/js/app.js - import CSS on top:

import './bootstrap'
import '../css/app.css'


Finally, in resources/views/welcome.blade.php leave also only JS, no CSS needed here:

@vite('resources/js/app.js')


Visually, nothing will change, but I see many developers doing this change, so I thought to include it here, so you wouldn't get confused.




Vite: Hot Module Reloading


For those not familiar with Vite, I want to tell you about its most impressive feature - you can change the code in .vue files, and Vite will not only automatically re-compile it (so you don't need to run any "npm run dev" or "npm run watch" again) but also will automatically refresh the browser.

So, you make the change in the .vue file, click the Cmd+Tab / Alt+Tab to get into the browser, and the changes are already there. No refresh is needed. Pretty impressive.



Complete and Continue  
Discussion

18 comments