uf0.0.0-alpha

Getting started

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.

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 aroundTypeScriptFlow
React syntax it understandsJSX and TypeScriptcomponent, hook, renders, match, Flow enum
TransformOxcMeta's Flow parser → Hermes lowering passes → the React Compiler crate → oxc
Bundler and dev serverVite and RolldownVite
Configvite.config.tsuf.config.js, in Flow
Maturitya funded product with a team behind it0.0.0-alpha, and it says so on every page

The row that matters is the first one. Everything else follows from it.

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. uf runs tasks and builds a project. It 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 any until 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's task runner is Vite's, and uf drives standard Vite, so a project already using Vite's ecosystem keeps it. What does not compose is the transform: a module is Flow or it is TypeScript, and uf's pipeline is the Flow one.

Next: Install