Originally published at hafiz.dev
Every Laravel starter kit now ships with Vite+. That landed in laravel/maestro#60 on 28 August, covering all 21 kit variants, and it deletes eslint.config.js, .prettierrc and .prettierignore in favour of one binary called vp.
If you are starting a new app, you get this for free and it works. The interesting question is the other one. What happens when you point vp migrate at an app you already have?
I tried it on a stock Laravel React starter kit. The first attempt refused to run at all, the second reported success while quietly failing halfway through, and the config file came out at 1,293 lines. None of that is a reason to avoid Vite+, but all of it is worth knowing before you run the command on something you care about.
What Vite+ actually is
One binary that swallows your whole frontend toolchain. Vite for the dev server and build, Rolldown for bundling, Vitest for tests, Oxlint in place of ESLint, Oxfmt in place of Prettier, tsdown for library packaging, plus its own Node and package-manager management.
So instead of npm run lint and npm run format:check and npm run types:check, you run vp check and it does all three in one pass. The test half is Vitest, which is the JavaScript side of your suite rather than a replacement for Pest on the PHP side.
It is beta, currently 0.3.1. It is also MIT licensed, which is worth saying clearly because VoidZero originally announced it as a paid product for startups and enterprises. They reversed that and open sourced the whole thing. If you were holding off because you expected a licence bill later, that concern is gone.
Installation does not go through npm:
curl -fsSL https://vite.plus | bash
There is no npx vp. If you try it, the CLI stops you and points at the installer. On macOS it drops into ~/.local/share/vite-plus, asks for no sudo, and adds a line to your shell config. vp implode --yes removes it again.
Attempt one: it refuses
A freshly created Laravel React starter kit today ships Vite 6. Run vp migrate against it and you get this.
Worth noting that the message and the docs disagree. The CLI asks for Vite 7 or newer. The migration guide says to upgrade to Vite 8 and Vitest 4.1 first. Follow the docs, since Vite+ 0.3 is built on Vite 8.
The good news is that the refusal is clean. It checked package.json, decided it could not proceed, and changed nothing. No half-migrated state to unpick.
So the first real step of any migration is not vp migrate at all. It is upgrading Vite, which for most existing Laravel apps is its own piece of work with its own breaking changes.
Attempt two: the report says success, the middle says otherwise
I upgraded Vite to 8.3.0 and ran it again.
Read the summary and the warning together, because they contradict each other. ✓ Dependencies installed in 21s sits four lines above Dependency installation failed (exit code 1).
What happened underneath is a peer dependency conflict, the JavaScript cousin of the Composer conflicts that block a Laravel upgrade. The kit ships @tailwindcss/vite@4.0.8, which declares a peer range of Vite 5 or 6. Once Vite is on 8, npm refuses to resolve the tree and bails out. The migration kept going regardless, rewrote every config file, and only mentioned the failure in a warning at the bottom.
The fix is the one the warning names:
vp install
And it works, for a reason that is easy to miss. The migration rewrote package.json so that vite is no longer the real Vite:
"devDependencies": {
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.1",
"vite-plus": "0.3.1"
},
"overrides": {
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.1"
}
vite is now an alias to VoidZero's own package, pinned with an overrides entry so nothing in the tree can ask for anything else. Tailwind's peer range is satisfied by fiat. That is how the conflict disappears on the second attempt, and it is the part of Vite+ that deserves a moment of thought before you adopt it, because every package that peers on Vite is now resolving against a VoidZero build rather than upstream Vite.
If you deploy from CI with npm ci and a lockfile, test that path specifically. The alias plus override combination is exactly the sort of thing that behaves differently under a clean install.
The diff
Six files, and it deletes two of them:
.prettierrc | 18 -
eslint.config.js | 44 -
package-lock.json | 7574 +-
package.json | 35 +-
tsconfig.json | 1 -
vite.config.js | 1286 ++++
6 files changed, 4463 insertions(+), 4495 deletions(-)
Nine devDependencies collapse into two. Out go eslint, @eslint/js, eslint-config-prettier, eslint-plugin-react, eslint-plugin-react-hooks, typescript-eslint, prettier, prettier-plugin-organize-imports and prettier-plugin-tailwindcss. In come vite-plus and the aliased vite.
The scripts get rewritten to match, so vite build becomes vp build, prettier --write becomes vp fmt, eslint . --fix becomes vp lint . --fix, and a prepare script is added to wire up git hooks.
One detail the summary glosses over. It claims Prettier was migrated to Oxfmt, and it deleted .prettierrc, but it left .prettierignore on disk with a warning suggesting you move those patterns into the config. So you finish the migration with a stray dotfile for a tool that is no longer installed.
Why vite.config.js is now 1,293 lines
This is the number that surprised me. The starter kit's config was 21 lines. After migration it is 1,293.
It is not gratuitous. The ESLint config, the Prettier config and the lint environment all got folded into one file, and most of the bulk is a single block: 1,127 browser globals written out one per line.
export default defineConfig({
staged: {
'*': 'vp check --fix',
},
lint: {
plugins: ['oxc', 'typescript', 'unicorn', 'react'],
categories: {
correctness: 'warn',
},
env: {
builtin: true,
},
globals: {
AbortController: 'readonly',
AbortSignal: 'readonly',
// ...1,125 more
},
},
Underneath that sits the rules block, then the formatter settings, then your actual plugins.
Credit where it is due on the translation. It carried the kit's real Prettier settings across rather than resetting to defaults, so printWidth: 150, tabWidth: 4, single quotes, the Tailwind class sorting with its clsx and cn functions, and the ignore pattern for the generated UI components all survived intact.
Your Vite plugins also get wrapped:
plugins: lazyPlugins(() => [
laravel({
input: ['resources/css/app.css', 'resources/js/app.tsx'],
ssr: 'resources/js/ssr.jsx',
refresh: true,
}),
react(),
tailwindcss(),
]),
lazyPlugins keeps the plugin array from being constructed when you are only running lint or format, which is how vp check stays fast without booting your whole build pipeline.
The practical cost is that vite.config.js is no longer a file you read. It is generated output that happens to live in your repo, and every future diff on it will be noise. If that bothers you, the globals block is the part to consider trimming by hand once you know which environments you actually target.
It builds, and then vp check fails
The build works, and quickly:
✓ built in 467ms
vp dev starts a normal dev server on port 5173 and picks up the Laravel plugin correctly. Two deprecation warnings come from vite:react-babel about esbuild and optimizeDeps.esbuildOptions now being Rolldown's job, which is upstream plugin lag rather than anything your app did.
Then vp check fails on a starter kit nobody has touched.
Two separate things are going on, and it is worth keeping them apart.
The formatting failures are a scope change. The old scripts were prettier --write resources/, so formatting only ever looked at the resources/ directory. vp check looks at the repository. That means it reformats files Prettier was never pointed at, and on this kit it rewrote both GitHub Actions workflow files, 76 lines each. Nothing broke, but a migration that quietly restyles your CI config is a surprise, and it explains the reviewer note on the Laravel PR about Inertia kits needing workflow updates. If your GitHub Actions pipeline asserts on formatting, that reformat is the kind of thing that turns a green build red for reasons unrelated to your code.
The four lint errors are a different story:
welcome.tsx:221:46 TS2322 '"plus-darker"' is not assignable to MixBlendMode
login.tsx:25:72 TS2344 LoginForm does not satisfy FormDataType
register.tsx:20:72 TS2344 RegisterForm does not satisfy FormDataType
reset-password.tsx:24:72 TS2344 ResetPasswordForm does not satisfy FormDataType
Vite+ did not cause these. I checked out the pre-migration commit and ran npx tsc --noEmit against it, and the same four errors are already there. The starter kit had no types:check script, so nothing in the normal workflow ever ran the type checker. Vite+ turns on typeAware and typeCheck by default, so the moment you migrate, four pre-existing type errors become visible.
That is an argument for Vite+, not against it. But it does mean your first vp check on a real codebase will probably fail, and the failures will look like the migration's fault when they are not.
Should you migrate an existing app?
For a new project, take it. The kits ship with it, it works, and one command in place of four npm scripts is a real improvement to the daily loop. That holds whichever kit you picked, though the Livewire kits changed least of all of them, since they have no TypeScript to lint, which is one more small point in the Livewire versus Inertia ledger.
For an app you already run, the honest sequence is:
-
Upgrade to Vite 8 first. This is the actual project.
vp migratewill not even start until you do, and the Vite 8 upgrade has its own breaking changes that have nothing to do with Vite+. -
Run the migration on a clean branch and read the whole diff, particularly
vite.config.jsand anything under.github/. -
Run
vp installimmediately, whatever the summary claims about dependencies. -
Expect the first
vp checkto fail, and triage it before assuming the migration broke something. -
Test your CI install path, since the aliased
viteand theoverridesentry are the parts most likely to behave differently undernpm ci.
The thing I would weigh hardest is the alias. Vite+ is MIT and the reversal on pricing was the right call, but adopting it means your vite dependency resolves to a VoidZero package and every peer range in your tree is satisfied against that instead of upstream Vite. For most apps that is a fine trade for the tooling consolidation. It is still a decision, not a formality, and it is not one the migration output asks you to make out loud.
Worth knowing too that none of this is in the Laravel documentation yet. The asset bundling docs do not mention Vite+, vp, Oxlint or Rolldown anywhere, even though the starter kits now ship all four.
FAQ
Do I have to migrate if I am on an older Laravel app?
No. Vite+ ships in new starter kits, and nothing in Laravel requires it. The existing vite plus laravel-vite-plugin setup keeps working, and the Laravel documentation still describes that as the standard path.
Why did vp migrate refuse to run on my project?
Most likely your Vite version. The CLI stops if package.json has anything below Vite 7, and the migration guide asks for Vite 8 and Vitest 4.1. The refusal is clean, so nothing is modified when it happens.
Is Vite+ free, or does it become paid later?
MIT licensed and free. VoidZero originally announced tiered pricing with a flat fee for startups and custom enterprise pricing, then reversed course and open sourced it. The projects it builds on, Vite, Vitest, Rolldown and Oxc, are all MIT as well.
Why is my vite.config.js suddenly over a thousand lines?
The lint environment gets inlined, including roughly 1,100 browser globals, along with the rules block and the formatter settings that used to live in eslint.config.js and .prettierrc. It is generated configuration rather than something you are expected to read.
Will vp check pass on my codebase after migrating?
Probably not on the first run, and often for reasons that predate the migration. Vite+ enables type-aware checking by default, so any type errors your old lint script never surfaced will appear immediately. Check them against tsc on your pre-migration commit before treating them as regressions.
What this comes down to
Vite+ in the starter kits is a straightforward win for new apps. For existing ones, vp migrate is not really the step that matters. Upgrading to Vite 8 is, and the migration is the short part at the end.
If you do run it, read the summary sceptically. A migration that prints "Dependencies installed" and "Dependency installation failed" in the same output has earned a careful look at the diff before you commit it.


Top comments (0)