Elevate your Laravel apps with Vue, Tailwind, and Vue Router
Do you keep your backend completely separate from your front-end or do you try to integrate the Vue and Laravel structure into one project? I prefer the latter. It’s not as intuitive as it should be. I used to love the UI presets before, where you can select either React or Vue and move on. Although not terrible, we do need to set this up properly.
Installing Vue
First, setup a new Laravel projects. I’m using Docker on a Mac, so my installation is pretty straightforward.
curl -s "https://laravel.build/laravel-vue" | bash
Once installed, cd into your directory and run ./vendor/bin/sail up
to start your project.
resources/welcome.blade.php
Prepare the welcome.blade.php
file by opening it and deleting most of the code from there. You’re just going to want a div
with an id="app"
attribute.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
</head>
<body class="antialiased">
<div id="app"></div>
</body>
</html>
We’ll need to revisit the welcome view soon.
vitejs/plugin-vue
In order to use Vue, we need to install the vite vue plugin. Open your docker container called laravel-vue-laravel.test-1
. This was the name of the project that I created. Go into the terminal. You can do this through your terminal, but I like doing it through the container since I can be sure that the correct versions are installed and I don’t have to worry about them on my computer.
Type the following command:
npm i vue@next
After that’s installed, run one more command to install the vue plugin:
npm i @vitejs/plugin-vue
If you get an error, you may need to run a command with the version:
npm i @vitejs/plugin-vue@3
vite.config.js
The vite.config.js
file in your main directory needs to be modified. We’ll need to import Vue
from our plugin and register it under plugins
. You’ll probably see a file that looks like this:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
});
The file should look like this after you add all of the necessary code.
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import Vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
Vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
}
}
}),
],
});
resources/js/App.vue
Time to create the App.vue
file. You can name it whatever you’d like as long as you know this is the entry point. I’ll start with a simple file that contains an h1
tag.
<template>
<h1>Welcome Page from Vue</h1>
</template>
<script>
export default {
name: 'App',
components: {},
}
</script>
<style>
</style>
resources/js/app.js
Next, we need to modify our app.js
file. It’s currently importing bootstrap.js
from the same directory, but we need for it to actually import our App.vue
. We’ll also need createApp
from vue
so that we can mount the content onto #app
id within resources/views/welcome.blade.php
. Your resources/js/app.js
file should look like this.
import { createApp } from "vue";
import App from "./App.vue";
createApp(App).mount("#app");
Renaming welcome.blade.php
I normally like to change the name from welcome.blade.php
to index.blade.php
. This also requires that you change the route in your route file, routes/web.php
, to point to index
.
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('index');
});
Including app.js in index.blade.php
The last modification to get this to work is to include the app.js
file in index.blade.php
. We can do this vith vite
.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
@vite(['resources/js/app.js'])
</head>
<body class="antialiased">
<div id="app"></div>
</body>
</html>
Make sure it works
Go back to your container and run npm run dev
. Visit your application in your browser, http://localhost
or http://0.0.0.0
, and see the Vue application running.
Installing Tailwind
We don’t want to stop here. We need to install Tailwind as well. The Tailwind website gives us really good instructions on how to do this.
Go back to your Docker container. If npm run dev
is still running, hit CTRL+C
to stop it. Execute the following commands:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
tailwind.config.js
Once complete, go back to your editor and open the newly created tailwind.config.js
file that’s located in your root directory.
Modify the content
section in the modeule.exports
. Your file should look like this.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
"./resources/**/*.vue",
],
theme: {
extend: {},
},
plugins: [],
}
resources/css/app.css
Tailwind also needs to be added to our app.css
file.
@tailwind base;
@tailwind components;
@tailwind utilities;
Include app.css in index.blade.php
Finally, we need to include the app.css
file in our index.blade.php
file. We can do that with vite
again.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
@vite(['resources/js/app.js', 'resources/css/app.css'])
</head>
<body class="antialiased">
<div id="app"></div>
</body>
</html>
Make sure it works
Go back to your container and run npm run dev
. Visit your application in your browser, http://localhost
or http://0.0.0.0
, and see the Vue application running with Tailwind installed now.
Taking the Vue Application for a Spin
Let’s make a components
directory within resources/js/
. Once created, make a new file called Header.vue
.
We’ll move our Welcome Page from Vue
header from App.vue
to Header.vue
. We can also add some scoped
styling to the h1
tag. To make sure that Tailwind works, we can add a p-4
class to our h1
tag.
<template>
<h1 class="p-4">Welcome Page from Vue</h1>
</template>
<script>
export default {
name: 'Header',
}
</script>
<style scoped>
h1 {
background-color: cornflowerblue;
color: white;
}
</style>
Modify App.vue
to include the Header.vue
component.
<template>
<Header />
</template>
<script>
import Header from './components/Header.vue';
export default {
name: 'App',
components: {
Header,
},
}
</script>
<style>
</style>
npm run dev
is active and refresh your page. You’ll see it functioning properly.Installing Vue Router
Let’s finish this by adding the vue-router
. We’ll need to install the dependency first. Go to the Docker container and stop the application from running (CTRL+C
). Install vue-router
dependency with the following command:
npm install vue-router
You can check your package.json
file to make sure all of your dependencies are added.
{
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"autoprefixer": "^10.4.13",
"axios": "^1.1.2",
"laravel-vite-plugin": "^0.7.0",
"lodash": "^4.17.19",
"postcss": "^8.4.20",
"tailwindcss": "^3.2.4",
"vite": "^3.0.0"
},
"dependencies": {
"@vitejs/plugin-vue": "^3.2.0",
"vue": "^3.2.36",
"vue-router": "^4.1.6"
}
}
web.php
We’re going to start running into some issues with the way our current web.php
file is configured. Let’s get that out of the way first so that we don’t have to worry about it later.
<?php
use Illuminate\Support\Facades\Route;
Route::get('/{all}', function () {
return view('index');
})->where("all", ".*");
Creating Home and About components
To test our routes, we’ll need to create a couple of components. I’m going to create two of the most common pages, Home
and About
.
<template>
<div class="p-20">Home Page</div>
</template>
<script>
export default {
name: 'Home',
}
</script>
<style scoped>
div {
background-color: coral;
color: white;
}
</style>
<template>
<div class="p-20">About Page</div>
</template>
<script>
export default {
name: 'Home',
}
</script>
<style scoped>
div {
background-color: darkseagreen;
color: white;
}
</style>
resources/js/router/index.js
Time to create our routes file. This will process all of our routes instead of web.php
. For this file, we need to:
- import
createRouter
andcreateWebHistory
fromvue-router
- import any page component that we need to define a route to, like
Home
andAbout
- create our routes constant and bind the component to it
- create our
router
and bindhistory
androutes
to it - export router
import {createRouter, createWebHistory } from "vue-router";
import Home from "../components/Home.vue";
import About from "../components/About.vue"
const routes = [
{
path: "/",
name: "home",
component: Home,
},
{
path: "/about",
name: "about",
component: About,
},
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
});
export default router;
resources/js/app.js
The exported router
now needs to be imported. It needs to be imported into our app.js
file. Once imported, we need to use
it. Add the use(router)
function before we mount
our app.
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router/index";
createApp(App)
.use(router)
.mount("#app");
resources/js/App.vue
Finally, we need to modify the App.vue
file so that it contains the contents that our router file returns. We are going to add <router-view />
directly underneath our <Header />
.
<template>
<Header />
<router-view />
</template>
<script>
import Header from './components/Header.vue';
export default {
name: 'App',
components: {
Header,
},
}
</script>
<style>
</style>
Take the full app for a spin
Go back to your container and run npm run dev
. Visit your application in your browser, http://localhost
or http://0.0.0.0
, and see the Vue application running with Tailwind and Vue-Router.
The main route, (/
), will return the Hom
Going to the about route, (/about
), will return the About
component.
I’m going to add a couple of links to the header so that we can easily switch between the pages.
<template>
<h1 class="p-4">Welcome Page from Vue</h1>
<div class="p-4">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
</template>
<script>
export default {
name: 'Header',
}
</script>
<style scoped>
h1, div {
background-color: cornflowerblue;
color: white;
}
</style>
We can now click between the two pages. Have fun.
Laravel Series
Continue your Laravel Learning.
Elevate your Laravel apps with Vue, Tailwind, and Vue Router
Laravel — P9.x – Vue,Tailwind, Vue-Router
In this guide, discover how to integrate Laravel 9.x with Vue, Tailwind, and Vue Router to build modern, responsive web applications. Streamline your front-end development with utility-first CSS, create modular components, and power seamless navigation across your single-page application.