Setup Laravel, Vue 3, Inertia JS & a few extras

Before we get started building our application we need to setup our development environment. At the time of this writing we’ll be installing laravel 9, Vue 3 and Inertia JS. Assuming you have composer and node js already installed on your system, you may proceed with these steps.

Create Laravel Project

To create a fresh new laravel project, open up your terminal window and type in the following:

composer create-project --prefer-dist laravel/laravel budget-app

Install Dependencies

Once that is done, we have to install the dependencies that we need inorder to run inertiajs

composer require inertiajs/inertia-laravel

Creating the Inertia middleware is the next step. This handles the requests and also helps us to share data with all our Vue views, similar to View::share().

php artisan inertia:middleware

The following file HandleInertiaRequests.php is created inside app/Http/Middleware folder. The followig middleware will need to be added to the web middleware group inside app/Http/Kernel.php:

'web' => [
    // ...

Now the Inertia JS on the client side needs to be setup. We’re using Vue 3, so we’ll install Inertia alongside with the Vue 3 adapter:

npm install @inertiajs/inertia @inertiajs/inertia-vue3

Let’s now install the inertia progress bar which gives us a visual on the loading of a page.

npm install @inertiajs/progress

Inertia uses Laravel’s routes, so we won’t need to use a client side router, but to make use of Laravel’s web.php routes, we have to pass them to the DOM somehow. The easiest way to do it to use Ziggy.

composer require tightenco/ziggy

Now we can use the @routes blade directive inside our blade template to expose the web.php routes to the client side.

Install Tailwind CSS

We’ll be using Tailwind CSS for styling our web application and thi sis the easiest to setup.

npm install -D tailwindcss postcss autoprefixer

Create tailwind config file:

npx tailwindcss init

Open up tailwind.config.js and enter the following code:

module.exports = {
    content: [
    theme: {
        extend: {},
    plugins: [],

The code is to tell tailwind JIT what clases that we’ll use in our templates and generate them.

We also have to add the Tailwind’s directives to resources/css/app.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Lastly we require Tailwind into webpack.mix.js which uses Laravel Mix to build our assets.

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

Install Vue JS 3

We will be using Vue JS 3 so to install it run the following command:

npm install vue@next

With Vue 3 it has 2 different API styles, Composite API and Options API. What are the differences between these two styles? With Composition API, we define a component’s logic using imported API functions. In SFCs, Composition API is typically used with <script setup>. With Options API, we define a component’s logic using an object of options such as datamethods, and mounted. Properties defined by options are exposed on this inside functions, which points to the component instance. You can find out more here with examples.

Setup the Root Blade Template

Now we have to setup our blade template app.blade.php to allow inertiajs to be executed. You may need to rename the blade template from welcome.blade.php to app.blade.php. This file is located in the resources/views folder

<!DOCTYPE html>
<html lang="en">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="{{ mix('/css/app.css') }}" rel="stylesheet" />
    <script src="{{ mix('/js/manifest.js') }}" defer></script>
    <script src="{{ mix('/js/vendor.js') }}" defer></script>
    <script src="{{ mix('/js/app.js') }}" defer></script>



From the code above you’ll see that there is no title tag, as the title tag needs to be dynamic so we add the @inertiaHead directive.

We have added the @routes directive to pass the Laravel’s routes in the document’s <head>.

In the <body> we only use the @inertia directive. This directive is used to pass the data-page attribute to the div element that is rendered.

Setup app.js

import { createApp, h } from 'vue'
import { createInertiaApp, Link, Head } from '@inertiajs/inertia-vue3'
import Layout  from "./Shared/Layout"
import route from "ziggy-js";

import { InertiaProgress } from '@inertiajs/progress'

const app = createInertiaApp({
    resolve: async name => {
        let page = (await import(`./Pages/${name}`)).default;

        if (page.layout === undefined) {
            page.layout = Layout;

        return page;
    setup({ el, App, props, plugin }) {
        const VueApp = createApp({ render: () => h(App, props) })
            .mixin({ methods: { route } })
            .component("Link", Link)
            .component("Head", Head)

    color: 'red',
    showSpinner: true,

What does this is to import Vue, Inertia, Inertia Progress and Ziggy and then create the Inertia App. We’re also passing the Link and Head components as globals because we’re going to use them a lot. Inertia will load our pages from the Pages.

Leave a Reply

Your email address will not be published.