Home
laravel
mix
upgrade
vite

Replace Laravel Mix with Vite

Posted by Chris Mellor Chris Mellor on June 29th, 2022

As of Laravel version 9.19, the framework has chosen to replace its front-end asset management tool Mix with the very popular tool Vite.

Getting started

This guide will explain how to upgrade an existing application with Mix to use Vite. Brand new installations of a Laravel app will have this already in place — no work required.

Remove Mix

First, remove Mix as it will now no longer be needed.

1npm remove laravel-mix

Then remove the webpack.mix.js file from your root.

Install Vite and the Laravel plugin

Now install Vite, along with the new Laravel plugin for it. Save them as dev dependencies.

1npm install --save-dev vite laravel-vite-plugin

Configuring the plugin

Create a new file in the root called vite.config.js and add this code to it

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel([
7 'resources/css/app.css',
8 'resources/js/app.js'
9 ]),
10});

Update scripts

The build scripts have changed with Vite and need to be replaced in the package.json file.

1"scripts": {
- "dev": "yarn run development",
- "development": "mix",
- "watch": "mix watch",
- "watch-poll": "mix watch -- --watch-options-poll=1000",
- "hot": "mix watch --hot",
- "prod": "yarn run production",
- "production": "mix --production"
+ "dev": "vite",
+ "build": "vite build"
11},

Compatibility with ES Modules

Vite can only support ES Modules, so any cases where you’re using the require() method in your Javascript now needs to be replaced with an import. Within a Laravel app, here is where these changes can be made

resources/js/app.js

-require('./bootstrap');
+import './bootstrap';

resources/js/bootstrap.js

-window._ = require('lodash');
+import _ from 'lodash';
+window._ = _;
-window.axios = require('axios');
+import axios from 'axios';
+window.axios = axios;
-// window.Pusher = require('pusher-js'); //
+// import Pusher from 'pusher-js'; //
+// window.Pusher = Pusher; //

Update Environment files

Your .env and the .env.example files must be updated too

-MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" //
-MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" //
+VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" //
+VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" //

The PUSHER_APP_* variables are also mentioned within the resources/js/bootstrap.js file, so it would be worth updating these too

-key: process.env.MIX_PUSHER_APP_KEY,
-cluster: process.env.MIX_PUSHER_APP_CLUSTER,
+key: import.meta.env.VITE_PUSHER_APP_KEY,
+cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,

Using the new Vite directive

This version of Laravel comes with a handy new Blade directive which is to be used to replace your existing links to your app.css and app.js files.

Remove any mention of these files and replace them with the new directive

1@vite(['resources/css/app.css', 'resources/js/app.js'])

Optional extras: Tailwind; Gitignore

If you’re using Tailwind CSS, a postcss.config.js file is required for the utility classes to be generated correctly with Vite. Run this command to add this file to your root with the necessary code

1npx tailwindcss init -p

Or you can add this manually by creating a new file called postcss.config.js and adding the below code

1module.exports = {
2 plugins: {
3 tailwindcss: {},
4 autoprefixer: {},
5 },
6}

When you generate the assets with Vite, it stores the files within a build folder within the public directory. If you don’t want these files to be in your VCS then ignore the folder in the root .gitignore file

1/public/build

Running locally

Now everything is set up, you can run the scripts as you’re used to and reap the benefits of faster build times

1npm run dev

You’ll see some differences from when running this with Mix installed. This will boot up a dev server running on port 3000 and will start listening for changes immediately, similar to running mix watch and you’ll see it runs a lot quicker.

Deployment

When deploying your app, be sure to update your deployment script (or however you do it) to use the new build script, replacing npm run prod

1npm run build

This compiles your assets into a public/build folder and the @vite() blade directive will inject it into your code. You can do this locally too if you wish.

🔥 Hot Module Reloading (HMR)

A cool feature of Vite is Hot Module Reloading, which means when you save a file, the browser will inject that code into the browser without reloading. This works out of the box when running the dev server.

Tip: Using Laravel Valet with HTTPS

If you’re using Laravel Valet with secured sites, it is advised to add this to your vite.config.js file

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel([
7 'resources/css/app.css',
8 'resources/js/app.js'
9 ]),
10 server: {
11 https: true,
12 host: 'localhost'
13 }
14});

⚠️ You may run into issues with your browser connecting to Vite — if so, visit the URL to the Vite server (usually https://localhost:3000) and accept the certificate pop-up warnings you’ll get. You should only need to do this once. Always restart the dev server when making any config changes.

Tip: Auto-reload Blade Files

⛔️ Update as of v0.3.0

The plugin now supports Blade reloading out of the box. The syntax is slightly different, as shown below

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
- laravel([
- 'resources/css/app.css',
- 'resources/js/app.js'
- ]),
+ laravel({
+ input: [
+ 'resources/css/app.css',
+ 'resources/js/app.js'
+ ],
+ refresh: true
+ }),
- {
- name: 'blade',
- handleHotUpdate ({ file, server }) {
- if (file.endsWith('.blade.php')) {
- server.ws.send({
- type: 'full-reload',
- path: '*'
- });
- }
- }
- }
28 ],
29 server: {
30 https: true,
31 host: 'localhost'
32 }
33});

The below is no longer relevant

One thing Vite cannot do is use HMR to inject code into Blade files, but there is a workaround that involves listening for changes to Blade files and doing a full page reload, but automatically. Add this snippet of code to your vite.config.js file.

1import { defineConfig } from 'vite';
2import laravel from 'laravel-vite-plugin';
3 
4export default defineConfig({
5 plugins: [
6 laravel([
7 'resources/css/app.css',
8 'resources/js/app.js'
9 ]),
10 {
11 name: 'blade',
12 handleHotUpdate ({ file, server }) {
13 if (file.endsWith('.blade.php')) {
14 server.ws.send({
15 type: 'full-reload',
16 path: '*'
17 });
18 }
19 }
20 }
21 ],
22 server: {
23 https: true,
24 host: 'localhost'
25 }
26});

Now when you update a Blade file, you should see the page reload automatically.

Using React and Vue

This guide is very basic and only explains how to upgrade a standard Laravel app that only uses Blade files. You can use this with React and Vue but that goes beyond the scope of this guide. If you want to know more about how to use Vite with React and Vue and SSR, check out the official upgrade guide on the Vite plugin repository — https://github.com/laravel/vite-plugin/blob/main/UPGRADE.md

Wrapping up…

  • Check out Laravel’s own documentation on using Vite
  • Thanks to @freekmurze for the information on using HMR with Blade files
  • Here is a PR I did for this website which shows all the changes I made to replace Mix with Vite - https://github.com/cjmellor/mellor.pizza/pull/35/files
+