Readiness: Implemented
Why uf
What uf is
uf is one binary that runs, builds, tests, formats and lints a Flow and React project. The argument is not that each of those is better on its own — it is that they stop being separate things you have to make agree.
What you will be able to do: say what uf is for, what one toolchain buys over several, the quality bar it holds itself to, and what it will not do.
What you need first: nothing installed. This is the first page of Why uf, for deciding whether to use it; Start is the path for trying it instead.
The problem it is aimed at
A Flow and React project today is an assembly job. Babel to strip the types, a
preset to understand component, a plugin for the React Compiler, Vite for the
dev server, a second config to tell Vite about the first one, Vitest with its
own transform pipeline that has to be configured the same way, Prettier for
formatting, ESLint with a parser plugin so it can read the syntax, and a
tsconfig.json you do not use but something insists on.
Every one of those is a place where two tools can disagree about what your code means. When they do, the error surfaces somewhere unrelated: a test that passes and a build that fails, or a formatter that reflows syntax the linter then rejects.
uf's position is that there should be exactly one answer to "what does this file mean", that it should be given once, in Rust, and that everything else should use it.
What that buys
One parse of the modern syntax. component, hook, renders, match and
Flow enums are understood by Meta's own Flow parser, compiled into the binary.
The dev server, the build, the test runner, the formatter and the linter all
read the same tree. There is no second parser to keep in step.
No Babel. Flow reaches JavaScript through the official Flow parser, ports of Hermes' own lowering passes, the official React Compiler crate in syntax mode, and oxc for JSX and codegen. Nothing in that list is a plugin you install.
One config. uf.config.js is the only config file. It is Flow, so it is
type-checked, and it configures the runtime, the router, the build, the test
runner and the formatter together.
One command. uf dev, uf build, uf test, uf fmt, uf lint,
uf check — nothing to add to package.json for any of them.
The quality bar
uf is not trying to win by adding a wrapper around every tool people already run. The bar is that the surfaces a Flow React project touches all agree about the same source file, and that the agreement is visible from commands you can put in CI.
| Surface | What should be true | How to check it |
|---|---|---|
| Formatting | Flow syntax is printed by the same parser that the rest of the toolchain reads | uf fmt --check |
| Linting | Framework mistakes are reported before a browser or bundler can hide them | uf lint --json |
| Type checking | Package exports, route declarations and generated surfaces resolve through Flow, not a parallel type story | uf check |
| Testing | Discovery, scheduling and reporting are owned by Rust; JavaScript only runs the test bodies | uf test --list, then uf test |
| Building | The Vite graph receives the same transforms, routes and RSC analysis the other commands saw | uf build --size-report |
That is why the docs are intentionally specific about losses and gaps. A page that says "implemented" should point at a command, a fixture, or a public package contract. A page that says "pending" should say what is missing, not turn a roadmap item into a feature by changing the tense.
For day-to-day work, the tight loop is:
$ uf fmt --check
$ uf lint --json
$ uf check
$ uf test --list
$ uf test
Those commands do not prove a deployment platform, a native app shell or a Flight payload. They prove the part uf owns: one parser, one route model, one set of framework diagnostics, one test schedule, and one report a machine can read.
What it does not do
uf does not replace Vite, and does not want to. The dev server and the production build are Vite 8 — uf decides what Vite is handed and drives it over a JSON protocol. Vite's plugin ecosystem keeps working, and a project that outgrows uf's opinions can reach past them.
uf does not type-check your code. Flow does that, and uf check runs it.
uf is not finished. It is at 0.0.0-alpha, every release so far is a
prerelease, and parts of it lose to what they mean to replace — the test runner
is about 2.2 times slower than Bun's, and its own page says
so rather than leaving you to find out.
Where to go next
Why Flow is next: the language argument this page takes for granted. To try uf before deciding, Start installs it and builds a project.
Edit this pagedocs/app/guide/$page.mdx