Ember's build story has been moving for several years, and it has settled on a destination: Vite. New apps are generated with it, the Embroider work that started as a compatibility layer over Broccoli now exists mainly to feed a standard bundler, and the ecosystem's tooling assumptions are shifting accordingly. If you run an app that was generated in 2015 and still builds through ember-cli-build.js, this is the change that will eventually reach you.
It is not urgent. Broccoli builds still work, and an app on a current LTS with a working build is not in danger. But the direction matters when you are planning a year of modernization work, because the Vite move is downstream of things you probably already want to do, and it pays for itself in the one place teams feel every day: local feedback time.
What actually changes
The classic pipeline builds your whole app — app tree, addon trees, vendor — through Broccoli into dist, then serves it. Rebuilds are incremental in principle and frequently slow in practice, and the output is one large bundle set assembled by rules that are specific to ember-cli.
A Vite-based build is ordinary modern JavaScript tooling. In development, modules are served natively and transformed on demand; you get fast start-up and near-instant hot updates because nothing rebuilds the world. In production, Rollup does the bundling, with the code splitting, asset handling, and plugin ecosystem any other framework's app gets. Ember-specific behavior — resolving my-app/components/foo, compiling templates, handling addon formats — arrives as Vite plugins rather than as a bespoke build system.
The practical consequences for a mature app:
- Resolution becomes static. The classic build could defer a lot of "what does this name refer to" work to runtime. Vite needs to know at build time. This is the same constraint Embroider's static flags impose, which is why they are the prerequisite and not an optional polish step.
- Addons must be readable by a standard bundler. V2 addons are already shipped in plain ES modules. V1 addons are handled through a compatibility layer, and the awkward ones — those that write files during the build, reach into the app tree, or rely on
treeForhooks doing something clever — are where the real work sits. - Anything that assumed the
distlayout gets re-examined. Custom fingerprinting, CSP header generation, per-environment asset rewriting, deployment scripts that pattern-match filenames. This is usually the most under-scoped part of the job, because it lives outside the app repo.
The prerequisites, in order
There is no shortcut that starts at Vite. The order is fixed by the constraints above:
- Get to a recent LTS. The Vite integration targets current Ember; running it against an app several LTS lines back means fighting two problems at once. Do the upgrade work first, one LTS at a time.
- Finish the Octane conversion in the code Vite has to understand. Classic constructs are not fatal, but a codebase still leaning on implicit resolution and dynamic lookup is a codebase that will fail static resolution in a hundred small places.
- Reach Embroider with the static flags on.
staticHelpers,staticModifiers,staticComponents— earning those is the actual migration. If you have already ratcheted through them, the Vite step is comparatively short. If you have not, that is your project, and the Vite switch is its last chapter. - Inventory the v1 addons that resist. For each one: is there a v2 replacement, can it be vendored as a few files you own, or is the feature deletable? This list, more than anything in the app code, determines the effort range.
How we sequence the switch itself
Once the prerequisites are met, the move is a short, sharp piece of work rather than a long grind — and it should be done on a branch that stays rebasable against main, because the app keeps shipping while this happens.
Build the Vite configuration and get ember serve's replacement running locally, with the test suite green in a real browser run. Then the CI pipeline: production build, output inspection, bundle size compared honestly against the old output. It will differ; splitting behavior changes, and some apps get bigger before they get smaller. Then the deployment surface — asset paths, fingerprints, source maps, CSP, anything downstream that reads dist.
Ship it behind whatever your normal release safety looks like, with the old build still buildable for a release or two. Keeping both pipelines alive costs something; it costs less than discovering a fingerprinting difference in production on a Friday.
Deciding whether to do it now
The honest answer for many apps is: not this quarter.
Do it sooner if your team is losing real time to rebuild latency, if you are already mid-Embroider and the static flags are on, or if you are hiring and want the local dev experience to look like every other job a candidate has had. Fast feedback is not a vanity metric — it changes how often people run the tests.
Wait if you are several LTS lines back, if half the app is still classic, or if your build carries heavy custom logic that nobody currently employed wrote. In those cases the Vite move is a symptom of a bigger project, and doing the bigger project in order gets you there anyway.
And if you are seriously considering a migration off Ember inside the next year or two, think carefully about the size of the investment. A faster Ember build helps every day you still have an Ember app — which may well be several years, even under a strangler-route plan — but it is not a step toward the exit, and it should be budgeted as maintenance rather than as progress on the migration.
Either way, the useful question is not "should we be on Vite." It is "what is between us and it, and do we want those things for their own sake?" Usually the answer is yes: a current LTS, an Octane codebase, static resolution, and an addon list you understand. Vite is what you get at the end of that work, not instead of it.