Readiness: Implemented
Why uf
uf and Vite+
Vite+ is the toolchain uf will be compared to, and the comparison is simpler than it looks: they answer different questions. Vite+ unifies the JavaScript and TypeScript toolchain. uf makes Flow's React syntax usable.
What you will be able to do: say where uf and Vite+ overlap and where they differ, what Vite+ has that uf does not, and whether the two can be used together.
What you need first: nothing installed. uf compared covers the other tools people arrive from.
uf is not a competitor to Vite
Start here, because it is the thing most often assumed the other way round.
uf's dev server is Vite. Its production build is Vite. uf resolves your
config, decides what Vite should be handed, starts it on a JavaScript host and
drives it over a JSON protocol. Vite's plugin ecosystem keeps working, because
it is really Vite — plugins in uf.config.js is a list of Vite plugins.
Replacing Vite would have meant rebuilding the best-tested bundler pipeline in the ecosystem in order to change one thing about it. The one thing uf changes is what happens to a module before Vite sees it.
Where they differ
| Vite+ | uf | |
|---|---|---|
| Language it is built around | TypeScript | Flow |
| React syntax it understands | JSX and TypeScript | component, hook, renders, match, Flow enum |
| Transform | Oxc | Meta's Flow parser → Hermes lowering passes → the React Compiler crate → oxc |
| Bundler and dev server | Vite and Rolldown | Vite |
| Config | vite.config.ts | uf.config.js, in Flow |
| Framework | none of its own | a router, streaming server rendering, server actions, deploy adapters and --compile |
| Tasks | vp run, across a workspace's packages, with a cache | uf run: a dependsOn graph run up to a concurrency limit, cached on the inputs each task declares, with --why for each decision; uf run#member reaches one member's own tasks, and there is no --filter across members yet (#967) |
| Dependencies | vp install, add, remove, update, outdated, why and the rest, over your package manager | uf install, add, remove, update, why, ls, audit, search, patch and catalog, over your package manager, with dependencies' install scripts off unless uf pm approve-builds names them on a manager that keeps such a list |
| A package's binary | vp exec and vp dlx download and run it | uf exec, or ufx, runs what the project installed and fetches a package only with --yes |
| Agents | vp config sets up agent integration | uf mcp: eight tools over stdio — lint, check, test, the route table, explain, and the two that write, formatting and safe lint fixes |
| Maturity | a funded product with a team behind it | 0.0.0-alpha, and it says so on every page |
The Vite+ column is what its guide lists. The first row is the one that decides the question for most readers; the rest say what else each of the two has chosen to own.
What "TypeScript-first" costs you if you write Flow
A TypeScript-first toolchain does not refuse Flow — it simply has no path for
it, so you build one. That means Babel back in the pipeline: a preset to strip
the types, a plugin to understand component and hook, another for the React
Compiler, and then the same syntax explained again to the test runner, the
linter and the formatter, each in its own configuration.
Every one of those is a place two tools can disagree about what your code means, and the disagreement shows up somewhere unrelated: a test that passes and a build that fails, or a formatter that reflows syntax the linter then rejects.
uf's answer is that the parse should happen once, in the binary, and everything should read that tree. Why Flow is the longer form of the argument.
What Vite+ has that uf does not
Being straight about this is more useful than a feature table.
A team, a company and a support contract. uf is pre-release software with no stability guarantee. If you are choosing a toolchain for work that has to keep running in three years, that difference is the whole decision.
The TypeScript ecosystem. Every typed package on npm, every editor integration, every answer to every question. uf inherits Flow's ecosystem, which is much smaller. Why Flow does not pretend otherwise.
Monorepo tooling at scale. vp run runs a task across a workspace's
packages. uf run runs one project's task graph, cached, and uf run#member
reaches one member's own tasks — but there is no --filter to run a task in
several members, and no way for one member's task to depend on another's
(#967). uf has not been near a
thousand-package repository.
When uf is the right choice
- You write React and you want the syntax Flow has for it: props inferred from
a parameter list, the rules of hooks enforced by the checker, components that
can require particular children, exhaustive
match, real enums. - You would rather have one binary and one config file than six tools that have to be taught the same thing.
- You can live with pre-release software, and with an ecosystem where a package
without Flow types is
anyuntil somebody writes them.
If you write TypeScript, Vite+ is a better answer than uf will ever be for you, and this page is not trying to talk you out of it.
Can they be used together?
Partly, today. uf drives standard Vite, so a project already using Vite's plugins keeps them. What does not compose is the transform: a module is Flow or it is TypeScript, and uf's pipeline is the Flow one. And the commands overlap rather than stack — both have a dev server command, a test runner, a linter, a formatter, a task runner and a package-manager front end — so a project runs one of them for those, not both.
Where to go next
Benchmarks is next: every uf command timed beside Vite+ and the other tools on one machine, with the rows uf loses first.
Edit this pagedocs/app/guide/vite-plus/$page.mdx