Using Vite with Inertia — Laravel, Vue & Tailwind
Tags: laravel, vue, tailwindcss, inertiajs
Published: 2021-07-11 23:24:57

**TLDR; 5800ms (Laravel Mix) to 224ms (Vite⚡️) on hot-reload!**

We’ve been using Laravel-Mix for years and it’s been doing pretty good. However, recently we decided to build a project using Inertia.js (which was an awesome decision).

As the project started to grow, the development became a pain to wait for the webpack compiling.

Then, we decided to give [Vite](https://vitejs.dev) (from Vue’s creator Evan You) and the results were… ASTONISHING! I’ve been seeing Vite around in Twitter, but to be honest with you, I was not expecting THIS MUCH OF SPEED! 🚀

### Laravel-Mix was getting too so slow. 🐢

Benchmark Test on Hot Reloading _(with 16" MBP 64gb ram, 2.4 GHz 8-Core Intel Core i9)_

```
|    Compilation     | Laravel Mix | Vite  |
|--------------------|-------------|-------|
| Initial Compile    | 13257ms     | 636ms |
| Change Reflection  | 5848ms      | 224ms |
```

That’s like ‘20x for initial compilation’ and ‘25x when code change’ 😲

We are fascinated by results, so let me tell you how to set it up, so you can try it out too.

### Migrating To Vite⚡️

- First, you’ll need to install Vite: 
```
npm install vite
```

- Then, install Tailwind 
```
npm i tailwindcss postcss autoprefixer -D
```

- Create “vite.config.js” and “postcss.config.js” in your project base

```
const { resolve } = require('path');
import vue from '@vitejs/plugin-vue';

export default ({ command }) => ({
  base: command === 'serve' ? '' : '/dist/',
  publicDir: 'fake_dir_so_nothing_gets_copied',
  build: {
    manifest: true,
    outDir: resolve(__dirname, 'public/dist'),
    rollupOptions: {
      input: 'resources/js/app.js',
    },
  },

  plugins: [vue()],

  resolve: {
    alias: {
      '@': resolve('./resources/js'),
    },
  },
});
```

```
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }, 
}
```

_For the sake of completeness, here is Tailwind config (JIT is amazing as well!)_

```
module.exports = {
  mode: "jit",
  purge: ['./resources/**/*.{js,jsx,ts,tsx,vue,blade.php}'],
  theme: {},
  variants: {},
  plugins: [],
}
```

- And finally, you need to configure your app.js for Vite to work with Inertia.js. (The production compiling part kept me in the dark for a few hours)

```
import 'vite/dynamic-import-polyfill';
import '../css/app.css';
import { createApp, h } from 'vue'
import { App, plugin } from '@inertiajs/inertia-vue3'

let asyncViews = () => {
  return import.meta.glob('./Pages/**/*.vue');
}

const app = createApp({
  render: () => h(App, {
    initialPage: JSON.parse(el.dataset.page),
        resolveComponent: async name => {
            if (import.meta.env.DEV) {
                return (await import(`./Pages/${name}.vue`)).default;
            } else {
                let pages = asyncViews();
                const importPage = pages[`./Pages/${name}.vue`];
                return importPage().then(module => module.default);
            }
        }
    })
})
```

### Few things to keep in mind are:

- You can’t use `require("file")` syntax, so you always need to use `import * from file.js`

- You need to specify file extensions when importing Vue components, like `import FormInput from "@/Shared/Form/FormInput.vue"`

- "app.js" is the only point of entry for your app, so you need to import your app.css file in your app.js.

**…and your front-end is ready 🎉**

-----

### Setting up Laravel and package.json scripts

I wanted to run “hot reloading” as well as “production” environment interchangeably in my local, so I came up with the below solution. (If you figure out a better way, I’d be happy to hear)

Vite in 'dev mode' creates a local server in https://localhost:3000 (which can be configured in vite.config.js) whereas in ‘production mode’, it creates files in 'public/dist'.

- Edit your ‘package.json’ file accordingly:

```
"scripts": {
    "predev": "printf \"dev\" > public/hot",
    "dev": "vite",
    "preprod": "printf \"prod\" > public/hot",
    "prod": "vite build"
},
```

`npm run vite` is hot reloading itself and npm run dev is just for alias. The “pre” hooks are used to create a file in public directory so that the backend can figure out which mode is running.

Finally, you need to create a helper to resolve the path in your blade 
— just like Laravel Mix’s `{{ mix('/js/app.js') }}` helper.

You can create this php file in 'app/Helpers/vite.php' (or anywhere you like)

```
<?php

use Illuminate\Support\HtmlString;

if (! function_exists('vite_assets')) {
    /**
     * @return HtmlString
     * @throws Exception
     */
    function vite_assets(): HtmlString
    {
        $devServerIsRunning = false;

        if (app()->environment('local')) {
            try {
                $devServerIsRunning = file_get_contents(public_path('hot')) == 'dev';
            } catch (Exception) {}
        }

        if ($devServerIsRunning) {
            return new HtmlString(<<<HTML
            <script type="module" src="http://localhost:3000/@vite/client"></script>
            <script type="module" src="http://localhost:3000/resources/js/app.js"></script>
        HTML);
        }
        $manifest = json_decode(file_get_contents(
            public_path('dist/manifest.json')
        ), true);
        return new HtmlString(<<<HTML
        <script type="module" src="/dist/{$manifest['resources/js/app.js']['file']}"></script>
        <link rel="stylesheet" href="/dist/{$manifest['resources/js/app.js']['css'][0]}">
    HTML);
    }
}
```

- And include it to your `composer.json`

```
"autoload": {
    "psr-4": {...},
    "files": [
        "app/Helpers/vite.php"
    ]
},
```

[make sure to run:`composer dump-autoload`]

And finally add it to your master.blade.php

```
<!DOCTYPE html>
<html>
<head>
    <!-- Stuff -->
    {{ vite_assets() }}
    <!-- More Stuff -->
</head>
```

-------

#### 🏁 You are all set. Enjoy the super-speed compiling times ⚡️

-----

I believe this will change your development experience as drastic as it did to me! 🚀

I’m really curious about your compiling speeds, please leave a comment. 💬