Updated June 2026. Tested on Laravel 13 and Vite 5. If you are coming from Laravel Mix, this is the replacement; Mix is no longer the default and Vite is what ships today.
If you used Laravel a few years ago you bundled assets with Laravel Mix, a friendly wrapper over Webpack. Laravel has moved on. New apps ship with Vite, which is much faster, especially in development, where it serves your code instantly instead of rebuilding a bundle on every change. This post covers how Vite works in a Laravel app, and what is different if you are arriving from Mix.
What you start with
A fresh Laravel app already has Vite wired up. There is a vite.config.js in the root and the laravel-vite-plugin in package.json. The config points at your entry files.
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,
}),
],
})
refresh: true reloads the browser when you change a Blade file, which is a nice touch you did not get for free with Mix.
Load assets in Blade
Instead of pointing at compiled file paths, you use the @vite directive and list your entry files. Vite works out the rest, including cache busting.
<!DOCTYPE html>
<html>
<head>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
{{ $slot }}
</body>
</html>
In development this points at the Vite dev server. In production it points at the built, versioned files. You do not manage that switch yourself.
The two commands
npm run dev
This starts the Vite dev server. Keep it running while you work. Changes appear in the browser immediately, and your app's @vite directive talks to it automatically.
npm run build
This compiles and minifies everything into public/build for production, with hashed filenames for cache busting. This is the command your deploy runs.
Using Vue or React
Add the framework plugin and point Vite at it. For Vue:
npm install @vitejs/plugin-vue
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
laravel({ input: ['resources/js/app.js'], refresh: true }),
vue(),
],
})
Then mount your app in resources/js/app.js as usual.
Coming from Laravel Mix
The mental shift is small but real.
webpack.mix.jsbecomesvite.config.js. Instead of chainingmix.js().sass(), you list inputs in the config.mix()in your Blade becomes@vite([...]).npm run watchbecomesnpm run dev, which is a live dev server rather than a file watcher rebuilding a bundle.- Reference assets with absolute paths from
resources, and import your CSS from your JS entry rather than compiling it separately.
There is an official Mix to Vite migration guide for the details, but for most apps it is moving a few lines of config and swapping the Blade directive. The payoff is a dev experience that feels instant. Questions welcome in the comments.
All comments ()
No comments yet
Be the first to leave a comment on this post.