Set up Inertia, Vite, Vue3, TypeScript / Build our first Page

Set up Inertia, Vite, Vue3, TypeScript / Build our first Page

·

5 min read

Updated version for Laravel 10 / Twill 3 on Apr 14, 2023

Front-end tools installation

Laravel comes with a default package.json file to build the front-end assets and already integrates Vite and the Laravel Vite packages.

We can now install all the needed dependencies via yarn, but you can of course use npm if you prefer.

# Vue 3
yarn add vue

# Vite plugin Vue
yarn add vite @vitejs/plugin-vue --dev

# TypeScript
yarn add typescript --dev

# Inertia Vue 3 adapter (with @inertiajs/core and axios as a dependency)
yarn add @inertiajs/vue3

Configurations

Inertia

We need a TypeScript file to boot our Inertia application, so let's create app.ts file in /resources/js/ (you can rename the previous default app.js - or name it like you want and adapt the other files accordingly - and delete the bootstrap.js file).

You can refer to the official documentation for more details.

For an easy start, we will just create and mount a basic Vue / Inertia app:

import { createApp, h, type DefineComponent } from 'vue'
import { createInertiaApp } from '@inertiajs/vue3'
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers'

createInertiaApp({
  resolve: async (name: string) => {
    let page = await resolvePageComponent(`../views/Pages/${name}.vue`, import.meta.glob<DefineComponent>('../views/Pages/**/*.vue'))

    return page
  },
  setup({ el, App, props, plugin }) {
    createApp({ render: () => h(App, props) })
      .use(plugin)
      .mount(el)
  },
})

What it does

  • Import Vue and Inertia packages

  • Create the application, looking for Pages Vue Components (that replace standard Laravel Blade files) in /views/Pages when rendering from Laravel Controller

Vite

Laravel comes with a default vite.config.ts file in our project root folder with laravel plugin configured that we are going to edit:

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.ts'],
            refresh: true,
        }),
        vue({
            template: {
                transformAssetUrls: {
                    // The Vue plugin will re-write asset URLs, when referenced
                    // in Single File Components, to point to the Laravel web
                    // server. Setting this to `null` allows the Laravel plugin
                    // to instead re-write asset URLs to point to the Vite
                    // server instead.
                    base: null,

                    // The Vue plugin will parse absolute URLs and treat them
                    // as absolute paths to files on disk. Setting this to
                    // `false` will leave absolute URLs un-touched so they can
                    // reference assets in the public directory as expected.
                    includeAbsolute: false,
                },
            },
        }),
    ],
});

What it does

  • Import Vite and its Vue 3 SFC plugin

  • Define the configuration (Official Documentation)

    • plugins:

      • laravel: adapt the location of the application entry point

      • vue: use the Vite Vue plugin

TypeScript

We create a tsconfig.json in our project root folder:

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "useDefineForClassFields": true,

    // Required in Vue projects
    "jsx": "preserve",

    // `"noImplicitThis": true` is part of `strict`
    // Added again here in case some users decide to disable `strict`.
    // This enables stricter inference for data properties on `this`.
    "noImplicitThis": true,
    "strict": true,

    // Required in Vite
    "isolatedModules": true,
    // For `<script setup>`
    // See <https://devblogs.microsoft.com/typescript/announcing-typescript-4-5-beta/#preserve-value-imports>
    "preserveValueImports": true,
    // Enforce using `import type` instead of `import` for types
    "importsNotUsedAsValues": "error",

    // A few notes:
    // - Vue 3 supports ES2016+
    // - For Vite, the actual compilation target is determined by the
    //   `build.target` option in the Vite config.
    //   So don't change the `target` field here. It has to be
    //   at least `ES2020` for dynamic `import()`s and `import.meta` to work correctly.
    // - If you are not using Vite, feel free to override the `target` field.
    "target": "ESNext",

    // Recommended
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    // See <https://github.com/vuejs/vue-cli/pull/5688>
    "skipLibCheck": true,

    "types": ["vite/client", "node"],

    "baseUrl": ".",
    "paths": {
      "@/*": ["resources/*"]
    },
    "include": [
      "resources/js/**/*.ts",
      "resources/js/**/*.d.ts",
      "resources/**/*.vue"
    ]
  }
}

What it does

  • Use default compiler options given by Vue

  • Defines the include folders: all .ts, .d.ts and .vue files in our resources folders

Scripts definition

All the configuration is set, let's have a look at the Vite scripts in our package.json

    "scripts": {
        "dev": "vite",
        "build": "vite build"
    },

What it does

  • dev: start Vite server

  • build: build production bundles

Of course, you can name them like you want and adapt the scripts according to your build process.

We can now start our development environment:

yarn dev

Or build for production:

yarn build

Root View set up

To integrate Inertia and load our assets on the first page visit, we need a standard view Blade file. So we create app.blade.php in /resources/views/ folder (you can use and rename the default welcome.blade.php).

app.blade.php is the default view used by Inertia, you can change it in the Inertia Middleware or call Inertia::setRootView() in your code.

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        @vite('resources/js/app.ts')
        @inertiaHead
    </head>
    <body>
        @inertia
    </body>
</html>

What it does

  • Add the @vite() Blade directive in the head to reference our Vite entry point

  • Add the @inertiaHead Blade directive in the head

  • Add the @inertia() Blade directive in the body

Time to play and display our first Page

All that remains to do is:

  • Create a Laravel Controller with its route

  • Create a Vue Page

Laravel

We will create a simple Controller, its only task is to render the Index Page (that Inertia will resolve in our /resources/js/app.ts and that will look for /resources/views/Pages/Index.vue file).

/app/Http/Controllers/IndexController.php

<?php

namespace App\Http\Controllers;

use Inertia\Inertia;
use Inertia\Response as InertiaResponse;

class IndexController extends Controller
{
    public function __invoke(): InertiaResponse
    {
        return Inertia::render('Index');
    }
}

/routes/web.php

<?php

use App\Http\Controllers\IndexController;
use Illuminate\Support\Facades\Route;

Route::get('/', IndexController::class)->name('index');

Vue

We will start displaying a welcome message and write something in the console.

/resources/views/Pages/Index.vue

<template>
  <div>
    <h1>Welcome</h1>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  setup() {
    console.log('Page - Index')
  },
})
</script>

Or with the SFC <script setup> (compile-time syntactic sugar for using Composition API inside Single File Components: https://vuejs.org/api/sfc-script-setup.html)

<script setup lang="ts">
  console.log('Page - Index')
</script>

<template>
  <h1>Welcome</h1>
</template>

RUUUUUUN

Start Vite

yarn dev

Go to the web root of your project in a browser (probably http://localhost)

and...

And maybe should we validate the production build

First we build our assets.

yarn build

Now refresh your browser and you can see that the production JavaScript files are loaded


We'll do our best to provide source code of the serie on GitHub