Laravel Mix and Vite in one Laravel project: how to use them together?

Table of contents:

# Introduction to the problem

I have a legacy Laravel project that uses Laravel Mix for asset compilation. I needed to use Vite for the Filament package. I had to use both Laravel Mix (is built on the Webpack bundler) and Vite in the same project. The problem is that Vite uses modern ES modules, and Webpack uses CommonJS modules.

# An error after adding Vite to the project

By default, JavaScript files are treated as CommonJS modules. To use Vite, I had to change the configuration to treat JavaScript files as ES modules. I did this by adding the following declaration to the package.json file:

{
  "type": "module"
}

When I tried to compile assets with Laravel Mix (npm run production), I got the following error:

> production
> mix --production

[webpack-cli] Error [ERR_REQUIRE_ESM]: require() of ES Module /var/www/html/webpack.mix.js from /var/www/html/node_modules/laravel-mix/setup/webpack.config.js not supported.
webpack.mix.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename webpack.mix.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in /var/www/html/package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).

    at module.exports (/var/www/html/node_modules/laravel-mix/setup/webpack.config.js:11:5)
    at loadConfigByPath (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:1439:37)
    at async Promise.all (index 0)
    at async WebpackCLI.loadConfig (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:1454:35)
    at async WebpackCLI.createCompiler (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:1785:22)
    at async WebpackCLI.runWebpack (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:1890:20)
    at async Command.<anonymous> (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:912:21)
    at async Promise.all (index 1)
    at async Command.<anonymous> (/var/www/html/node_modules/webpack-cli/lib/webpack-cli.js:1372:13) {
  code: 'ERR_REQUIRE_ESM'
}

The error message gives a hint on how to solve the problem.

# Solutions

# Rename webpack.mix.js to webpack.mix.cjs

One of the solutions is to rename webpack.mix.js to webpack.mix.cjs:

mv webpack.mix.js webpack.mix.cjs

After renaming the file, this signals to Laravel Mix that the file is a CommonJS module.

# Rename vite.config.js to vite.config.mjs

You can keep webpack.mix.js and remove "type": "module"from package.json, but you have to rename vite.config.js to vite.config.mjs:

mv vite.config.js vite.config.mjs

Now, Vite config will be treated as an ES module file.