· via dev.to (home feed)
Farm.js AOT compiler skips React's render cycle for provable state updates
Farm.js's experimental ahead-of-time compiler analyzes React components at build time and, when it can prove which DOM nodes state touches, writes updates directly to the DOM, skipping React's render cycle entirely.

An ahead-of-time compiler that opts out of rendering
Farm.js, a full-stack React framework, has introduced an experimental ahead-of-time compiler that removes React's render cycle from updates it can fully analyze. According to a dev.to post by the project's author, when the compiler can prove at build time exactly which DOM nodes a piece of state touches, it writes updates straight to the DOM. When it cannot, the component stays on ordinary React — no new syntax and no different component model.
A browser playground running the real framework via WebContainers on StackBlitz means the whole thing can be tried without installing anything.
How the compiler works
A React update normally works by re-running the component function, building a new element tree, diffing it against the previous one, and committing the differences. For a simple counter — one button whose state can only ever change its text content and className — the author considers that machinery pure overhead.
The Farm compiler analyzes such components at build time and emits a compiled definition built around bindings: each binding records which state cell feeds a given text node or attribute, plus the function that computes its value. React still owns the first mount, so hydration, refs, and devtools all see a normal component. After mount, calling the setter walks the dependency graph and applies the affected bindings directly — no render, no reconciliation, no commit phase. The repository includes a runtime test that renders compiled and baseline versions side by side and asserts the compiled one updates with zero React renders.
Compiled components also carry a capability list. Components that need keyed lists, conditional ranges, or nested compiled components pull in feature modules for those features, and components that don't use a feature never import it, so unused structural runtimes are tree-shaken out. The author measured the runtime premium for direct-bindings-only output at 73.6% smaller than shipping the complete runtime.
Conservative fallbacks
The compiler is deliberately conservative: it only transforms a component when it can prove there is one stable host element tree and every supported state value maps to known text or attribute targets. Per the project's docs, constructs that always stay on React include unkeyed or index-keyed list rendering, effects and hooks other than the supported useState shape, ref, dangerouslySetInnerHTML, JSX spreads, multiple or conditional returns, props whose updates carry identity (objects, arrays, functions, elements), async components, and setters called outside JSX event handlers.
Fallback is silent by default, but diagnostics can log why a component was skipped, and a report option writes a .farm/react-compiler. file listing exactly which components compiled and which fell back, with reasons and counts. Any component can be opted out with a "use no compiler" directive. Enabling the compiler is a single experimental flag on the React renderer.
Self-reported benchmarks
The author benchmarked a js-framework-benchmark-style keyed table — create 1,000 rows, update every 10th label, advance selection, swap two rows, clear — on production builds driven by Playwright Chromium, with latency measured from click dispatch to a MutationObserver callback rather than quantized to frame boundaries. The runner installs published npm packages instead of linking the workspace, and it fails hard unless the compiler report proves the workload component actually compiled, so the comparison can't silently degrade into baseline versus baseline.
The median results: swap improved from 3.40ms to 0.50ms (6.80x), clear from 3.80ms to 0.70ms (5.43x), create by 1.17x, select by 1.20x — and update by exactly 1.00x. The author is explicit about that flat number: row-label updates were already cheap in React, and the compiled path cannot beat the DOM write itself. The wins appear where reconciliation is the cost — swaps, clears, and structural churn on keyed lists. Total CPU work (script, style and layout) per full action cycle dropped from 18.71ms to 9.05ms, roughly half. These are the author's own numbers; per-sample data and methodology sit in the repo's benchmarks directory.
The framework around the compiler
Farm is also a conventional full-stack framework: a Vite dev server, file-based app directory routing with the familiar page, layout, loading and error files plus dynamic and catch-all segments, streaming SSR, and Nitro production output. Routes are typed end to end — the framework generates a route-path union type from the app directory, so a typo'd internal link becomes a type error, and the types regenerate on dev start and whenever routes change.
Server functions take zod schemas for both input and output, validating the wire boundary in both directions, and a useServerFn hook exposes a formAction for progressive enhancement, with handlers that run on the server even when called from client components.
Why it matters
Farm's compiler is a concrete data point in the shift toward build-time optimization of UI updates. Rather than memoizing components to reduce re-renders, it removes the render entirely for updates it can prove safe, while falling back to stock React everywhere else — an opt-in optimization rather than a rewrite. Two engineering choices stand out: publishing the 1.00x result where the optimization does nothing, and a machine-readable compile report that turns "did my hot component actually compile?" into a checkable question. It remains experimental, single-author work with self-reported benchmarks, so it is best read as a promising direction for React tooling rather than something to bet production on today.
- #react
- #compilers
- #web-frameworks
- #performance
- #javascript