World’s largest virtual agentic engineering & quality conference
Flow is the JavaScript type checker from Meta, still shipping releases in 2026. See what it does, how it compares with TypeScript, and who should still use it.

Bhawana
Author
Last Updated on: August 8, 2026
Almost everything written about the Flow JavaScript type checker dates from 2014 to 2020 and reads as though the project stopped. It did not. The flow-bin package published version 0.326.0 on 5 August 2026, and it draws 388,477 downloads a week.
TypeScript, over the same week, drew 259,561,424. Both figures come from the npm registry, and together they frame the only question worth asking about Flow now: it is alive, it is small, and the useful answer is who it still makes sense for.
Overview
Flow is a static type checker for JavaScript from Meta that reads annotated source without running it and reports type errors. Files opt in individually, so adoption can be incremental. It is still actively released in 2026, and its ecosystem is a small fraction of TypeScript's, which is what decides most tool choices today.
What Should You Know Before Choosing It?
Who Should Still Use It?
Teams already running Flow at scale, where migration cost outweighs ecosystem friction, and codebases inside organisations that maintain their own type definitions anyway. For a new project starting today, TypeScript is the pragmatic answer, and this guide covers the migration path.
This deserves settling first, because the answer most search results imply is wrong. Here are the numbers, read from the npm registry for the same week.
| Package | Latest release | Downloads, week to 6 Aug 2026 |
|---|---|---|
| flow-bin | 0.326.0, published 5 August 2026 | 388,477 |
| typescript | 7.0.2, published 8 July 2026 | 259,561,424 |
Two conclusions follow, and they are not in tension. Flow is actively developed, with a release three days before this article was written. Flow is also roughly one six-hundredth the size of TypeScript by weekly downloads, which is the constraint that matters when you need type definitions for a package you did not write.
The version number is worth a note too. Flow has never reached 1.0, and 0.326.0 is a minor release in a long sequence rather than a sign of immaturity. Breaking changes do arrive in those minors, which is a real maintenance cost and a fair thing to weigh.
Flow reads your JavaScript, builds a model of what type each value can hold, and reports places where those types cannot be reconciled. It never executes the code, which puts it in the same family as a linter, checking properties of the source text rather than of the running program.
What separates it from a linter is inference. A linter matches patterns you configured. Flow reasons about values as they move through the program, so it can flag a mismatch several function calls away from where you introduced it, in code carrying no annotations at all.
Adoption is per file, which was a deliberate design decision and remains its most practical feature. A pragma comment marks a file for checking; without it, Flow ignores the file entirely.
// @flow
function greet(name: string): string {
return 'Hello, ' + name;
}
greet(42);
// Flow reports: Cannot call greet with 42 bound to name
// because number is incompatible with string.That means a repository can enable Flow globally and have it check only the twenty files someone opted in, then grow from there. Migrating a large codebase to a checker is otherwise an all-or-nothing commitment that teams postpone indefinitely.
The syntax will look familiar to anyone who has written TypeScript, with a handful of deliberate differences.
// @flow
// A maybe type. The question mark allows null and undefined,
// and Flow will not let you use it until you have checked.
function label(count: ?number): string {
if (count == null) return 'unknown';
return String(count);
}
// An exact object type. The pipes mean no extra properties
// are permitted, which is the default Flow chose and
// TypeScript did not.
type User = {| id: number, email: string |};
// A union, and a function typed against it.
type Status = 'active' | 'suspended' | 'closed';
function canLogIn(user: User, status: Status): boolean {
return status === 'active';
}The maybe type is where Flow earned its early reputation. Null handling was strict by default years before that became the common expectation, and a value that might be null cannot be used until the check exists. Exact object types work the same way, refusing extra properties rather than tolerating them.
Neither advantage is decisive now, because equivalent strictness is available elsewhere. They explain why Flow was chosen at the time, which is useful context if you have inherited a codebase that uses it.
| Question | Flow | TypeScript |
|---|---|---|
| What it is | A checker that reads annotated JavaScript. | A language with a compiler that emits JavaScript. |
| Adoption unit | Per file, via a pragma comment. | Per project, via configuration. |
| Third-party types | Few packages publish them. | Most packages ship or have community definitions. |
| Editor support | Works, with fewer integrations. | Deep, and largely automatic. |
| Null safety | Strict by default from early on. | Strict once the relevant compiler option is enabled. |
| Weekly downloads | 388,477 | 259,561,424 |
| Best fit | Codebases already on it. | Essentially everything else. |
The row that decides it is third-party types, and the reason is worth stating because it is not about language design at all. Types are most valuable at the boundaries of code you did not write. When most published packages ship TypeScript definitions and few ship Flow ones, choosing Flow means writing those definitions yourself or working without them at exactly the boundary where a checker helps most.
A short list, honestly drawn.
For a new project, the answer is TypeScript, and the reasoning is not that Flow's type system is worse. It is that a type checker's value comes from everything around it, and one of the two has a far larger everything.
If you have decided to move, move file by file. Both checkers ignore what is not theirs, so they coexist happily during a transition that may take months.
| Flow | TypeScript | Watch for |
|---|---|---|
| ?number | number | null | undefined | Flow's maybe type covers both null and undefined. Writing only one of them silently narrows the type. |
| {| a: number |} | An object type, plus care about excess properties | TypeScript has no direct equivalent to exact object types, so this is the conversion most likely to lose a guarantee. |
| mixed | unknown | A close match. Both require narrowing before use. |
| any | any | Identical, and a migration is the right moment to remove them rather than carry them across. |
| $ReadOnly<T> | Readonly<T> | Mechanical rename that codemods handle reliably. |
Codemods handle most of the syntax. What they cannot decide is nullability, because a Flow maybe type that was never really nullable in practice should become a plain type rather than a union, and only someone who knows the code can tell. Convert the leaf modules first, keep both checkers green on every commit, and do not mix a migration with a refactor.
Whichever checker you land on, its guarantee has a precise shape: values have the types you declared. That is genuinely valuable and it is not the same as the program being correct.
// @flow
// Fully typed. Flow reports nothing.
function orderTotal(subtotal: number, taxRate: number, hasCoupon: boolean): number {
if (!hasCoupon) {
return subtotal * (1 + taxRate);
}
return subtotal;
}
// orderTotal(100, 0.2, false) -> 120
// orderTotal(100, 0.2, true) -> 100 tax silently droppedEvery annotation is right. Every value has the type it claims. The function drops tax on any discounted order, and no type system in any language will report it, because nothing about the types is wrong. The intention is wrong, and intentions are not typed.
The same boundary applies to the browser. A component whose props typecheck can still render nothing, post to the wrong endpoint, or break on a viewport nobody tried. The distinction is the one covered in verification vs validation, and it is the reason a checker belongs in front of a test suite rather than instead of one.
That order is the practical takeaway. Types remove a class of defect cheaply and early. Executing the code removes a different class that no amount of annotation reaches, and the two do not overlap.
Note: A checker proves the shapes are right. Running the flow proves the total is right. Execute your suite across 3,000+ browser and OS combinations to catch what typing cannot. Start free
If you are on Flow and it is working, stay. The project ships releases, your codebase is annotated, and a migration is real engineering time spent on a change your users will never see. Revisit only when the ecosystem gap starts costing you, which usually shows up as writing your own definitions for a package everyone else gets typed for free.
If you are starting something new, use TypeScript, and understand that you are choosing an ecosystem rather than a type system. If you are migrating, go file by file, convert leaves first, and treat maybe types as the decisions rather than the codemod's job.
Either way, put a check behind the checker. Types stop the wrong shape reaching a function; running the code stops the wrong number reaching a customer. TestMu AI's automation cloud covers that second half across real browsers, the automation documentation covers wiring it in, and JavaScript unit testing covers the layer in between.
Author
Bhawana is a Community Evangelist at TestMu AI with over 3 years of experience creating technically accurate, strategy-driven content in software testing. She has authored 50+ blogs on test automation, cross-browser testing, mobile testing, and real device testing. She also serves as Product Marketing Manager for Kane CLI, the command-line tool that runs browser automation from the terminal using natural-language flows in a real Chrome browser. Bhawana is certified in KaneAI, Selenium, Appium, Playwright, and Cypress, reflecting her hands-on knowledge of modern automation practices. On LinkedIn, she is followed by 6000+ QA engineers, testers, AI automation testers, and tech leaders.
Did you find this page helpful?
More Related Blogs
TestMu AI forEnterprise
Get access to solutions built on Enterprise
grade security, privacy, & compliance