Laravel Mix to Vite Migration: What Breaks & How to Fix

The problem: You migrated an older Laravel app from Laravel Mix to Vite following the docs, but half your Blade views throw errors and your CSS/JS never load.

Environment: Laravel 10.x/11.x app originally scaffolded with Laravel Mix (webpack), migrating to laravel-vite-plugin, Node 20 LTS.

The Error

▲ resources/views/layouts/app.blade.php
BadMethodCallException
Call to undefined method Illuminate\Foundation\Application::mix()

  at vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:mix()
  // mix() is registered by the laravel-mix npm package's Blade helper —
  // once laravel-mix is removed, every <script src="{{ mix('js/app.js') }}">
  // call breaks immediately across every view that still uses it

Why It Happens

Mix and Vite aren't drop-in replacements for each other — they're two entirely different build tools with different config formats, different Blade helpers, and different output structures. Migrating means changing all of the following at once, and missing any one of them is what causes these errors:

  1. The Blade helper. mix('js/app.js') reads public/mix-manifest.json. @vite(['resources/js/app.js']) reads public/build/manifest.json. They are not interchangeable, and leaving even one old mix() call in a view breaks that page once the package is removed.
  2. The config file. webpack.mix.js defines your build with imperative calls like mix.js().sass().copy(). Vite uses vite.config.js with declarative entry points and plugins — nothing in webpack.mix.js is read once you switch.
  3. Source paths change meaning. Mix typically compiled from resources/js/app.js to public/js/app.js. Vite compiles to a hashed filename inside public/build/assets/, and the actual URL only exists via the manifest — you can no longer hardcode a path like /js/app.js anywhere.
  4. Static asset copying disappears. Mix's mix.copy('resources/images', 'public/images') has no direct Vite equivalent — Vite expects static assets referenced from JS/CSS to live in a way it can process, or to be placed directly in public/ and referenced without the build pipeline at all.

The Fix

1. Remove Mix, install Vite's Laravel plugin:

$ npm uninstall laravel-mix
$ npm install -D vite laravel-vite-plugin

2. Replace webpack.mix.js with vite.config.js:

// webpack.mix.js (delete this file)
mix.js('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

// vite.config.js
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,
        }),
    ],
});

3. Find and replace every mix() call in your Blade views:

<link href="{{ mix('css/app.css') }}" rel="stylesheet">
<script src="{{ mix('js/app.js') }}"></script>

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

4. For static assets Mix used to copy (images, fonts referenced directly in Blade, not imported in JS/CSS), skip the build pipeline entirely — move them straight into public/ and reference them with Laravel's normal asset() helper instead of trying to route them through Vite.

Step-by-Step

  1. Search the whole codebase for mix( across .blade.php files — every match needs to become a @vite([...]) call before you remove the package.
  2. Delete webpack.mix.js and create vite.config.js with your actual entry points listed in input.
  3. Update package.json scripts — replace "dev": "mix watch" and "production": "mix --production" with "dev": "vite" and "build": "vite build".
  4. Move any assets Mix was copying with mix.copy() directly into public/ and switch their references to plain asset() calls.
  5. If you compiled Sass/Less through Mix, install the matching Vite-compatible preprocessor package (sass for Vite works out of the box via its own resolver — no extra Mix-style config needed) and update your entry file extension accordingly.
  6. Run npm run dev locally, click through every page, and confirm no console errors — then run npm run build and check public/build/manifest.json exists before deploying.
⚠ Edge case: if your old Mix setup used mix.version() for cache-busting query strings on hardcoded asset URLs (common with third-party integrations that expected a specific static path), those URLs will simply 404 once Mix stops running — Vite doesn't generate anything at those old paths at all. Anything that depended on the exact old file structure (a service worker cache list, a hardcoded CDN path, an old app's inline <script src="/js/app.js">) needs to be found and updated manually; there's no compatibility shim between the two.

Hit a migration error not covered here? Post your webpack.mix.js on the Contact page and I'll help map it to Vite.

Advertisement
Advertisement