React Compiler Gets Rusty, Base UI Takes Over, and Node 26 Lands

Published on 30.04.2026

motyl.dev<div></div></>FRONTEND

TLDR

The React Compiler is getting a full Rust rewrite, with 1724 of 1724 test fixtures passing in the new port. Base UI is officially the new default primitive layer for shadcn/ui, meaning the Radix era is winding down. Node.js 26 dropped with Temporal enabled by default, pnpm 11 brought a SQLite store and proper isolated global installs, and Fresh 2.3 finally delivers on the "zero JS by default" promise it always made but never quite kept.

React Compiler Gets Ported to Rust

Here's something that flew a bit under the radar given how quietly it was merged: Joseph Savona opened a pull request to port the React Compiler from TypeScript to Rust. And it's not a prototype. The PR went from 1267 passing fixtures to 1724 of 1724. That's all of them. The Rust port passes the full test suite.

The motivation is what you'd expect: performance. The TypeScript compiler is already fast enough for most projects, but a Rust implementation opens the door to much tighter integration with native toolchains like SWC and OXC without the overhead of shelling out through JavaScript. The PR also describes a clean "JS to Rust boundary" philosophy: serialize only the core AST data structures from Babel, let Rust derive everything else.

What I find interesting is that the port happened in the open, incrementally, with a whole orchestration system to track which passes had been ported. They built tooling to run fixtures against both the TypeScript and Rust implementations simultaneously and diff the output. The process itself is worth reading about if you care about large-scale compiler work.

[compiler] Port React Compiler to Rust

Eighteen Months of React Compiler in Production

A thorough retrospective from Sascha Brink covers what the React Compiler era actually looks like on the ground, now that the initial hype has settled. The short version: greenfield is solved, brownfield is still a project.

The biggest thing I took from this post is the framing around what the compiler actually retired. Not performance benchmarks. A category of bug. "You forgot a useCallback dependency" is just not a conversation anymore. Nor is "should this component be memo'd?" The answer is always yes, and the compiler handles it. That's genuinely valuable even if you never see a dramatic speedup in your profiler.

The contested stuff is worth reading too. The "use no memo" escape hatch worries people because it has the same energy as @ts-ignore: fine as a release valve, dangerous as a permanent fixture. And the ecosystem reckoning around the Rules of React is ongoing. A lot of older libraries were quietly bending the rules, and the compiler just made it visible. They weren't broken by the compiler. They were already broken.

The React Compiler at Eighteen Months: The Arc, the Debates, and What's Next

How React Streams UI Out of Order

This is one of those posts that explains something you've been using without really understanding. The Suspense out-of-order streaming mechanism is elegant once you see it written out plainly: React sends what it has immediately, drops a <template> placeholder with an ID, then streams the resolved component as a hidden <div> later. A small inline <script> calls $RC() which swaps the hidden div into the placeholder slot using requestAnimationFrame.

The comment markers are the part I hadn't thought about before. <!--$?--> means the suspense boundary is pending. <!--$~--> means it's queued for swap. <!--$--> means resolved. The full lifecycle of a suspense boundary in three ASCII strings. There's also a fun section where the author intentionally puts a fake <template id="B:0"> in the DOM and watches React swap the wrong element in, which is the kind of thing that makes these internals click.

How React streams UI out of order and still manages to keep order

Accessibility in React: The Practical Checklist

This post from certificates.dev isn't breaking new ground, but it's a clean, comprehensive rundown of the a11y mistakes that show up in almost every React codebase. The <div onClick> problem, missing labels on icon buttons, focus going nowhere when a modal closes, dynamic updates that screen readers never hear about.

The useId section is the one I'd flag for teams that haven't updated their form patterns. Hardcoding IDs for label/input associations breaks with SSR and breaks with multiple component instances. useId fixes both. Same with the pattern for keeping role="alert" containers mounted but empty instead of conditionally rendering them. Screen readers monitor existing live regions. If you render the element fresh, they often miss the announcement entirely.

The styling tip is also genuinely useful: style based on ARIA attributes (aria-expanded, aria-disabled) instead of managing parallel state variables for visual styling. If the element has the right ARIA state, your CSS can just key off that directly.

Accessibility in React: Common Mistakes and How to Fix Them

Base UI Is the New Default for shadcn/ui

The shadcn ecosystem is shifting. Base UI, built by Colm Tuite (creator of Radix), the Floating UI team, and the Material UI team, is now the default primitive library when you scaffold a new shadcn/ui project. Radix still works, but the direction is clear.

The biggest API change is asChild to render. Where Radix used slot composition with asChild, Base UI uses an explicit render prop:

// Radix
<DialogTrigger asChild><Button>Open</Button></DialogTrigger>

// Base UI
<DialogTrigger render={<Button>Open</Button>} />

The guide also covers the unified package story: instead of @radix-ui/react-dialog, @radix-ui/react-dropdown-menu, and a dozen more individual packages, Base UI ships everything from @base-ui/react. That's a real quality-of-life improvement for dependency management.

The other change worth knowing about: Base UI uses ARIA attributes for state instead of Radix's data-[state=open] pattern. So data-[state=open]:rotate-180 becomes aria-expanded:rotate-180. This actually makes more sense, since you're styling based on the actual accessibility state rather than a library-specific convention.

Migrate from Radix UI to Base UI in 9 Easy Steps

TSRX: The JSX Successor Worth Watching

Ryan Carniato (creator of SolidJS) wrote about TSRX, the new alpha from Dominic Gannaway (creator of Inferno, former React and Svelte core). The pitch is that TSRX brings stateful templates to JavaScript without the constraints of existing DSLs.

The mechanical argument is about what changes when you add a conditional to a component. In JSX, adding an enabled prop to a component with an effect means touching three places: the effect condition, the cleanup condition, and the return value. In TSRX, you just wrap the block in an if (enabled) and indent. Cut and paste composability.

This matters more in signal-based rendering, where components don't have the hook rules that make conditional reactive primitives awkward in React. Whether this goes anywhere depends on adoption, but it's at minimum an interesting data point about where the community is thinking about syntax.

Why TSRX isn't just your Favorite Templating Language

StyleX v0.18

Facebook's atomic CSS-in-JS library got a solid release. The headline feature is stylex.env: compile-time environment values you can use anywhere in your styles without the constraints of defineConsts. Define an object like absoluteFill in your Babel config, use it as a spread in any stylex.create call, and it inlines at compile time with no runtime cost.

There's also a new create-stylex-app CLI, sx={} JSX shorthand that compiles to the same output as stylex.props(), self-referencing variables in defineVars (useful for derived design tokens), and stylex.attrs is back for SSR and non-React frameworks that expect a serialized style string.

The DevTools Chrome extension is worth mentioning too. It shows which styles apply to an element, where each style object comes from, and lets you override atomic CSS rules without touching your source. That's the missing piece for debugging atomic CSS in production.

StyleX v0.18

React Hook Form v7.74.0

Small release but one feature stands out: setValues now accepts a callback. Instead of passing a full form state object, you can pass a function that receives the current form data and returns the new state. That's the pattern you reach for when you want to update one field without clobbering the rest. The rest of the release is bug fixes: preserving field values on useController name changes, handling null parents when unregistering nested fields, and treating NaN correctly when valueAsNumber is true.

Release Version 7.74.0

JSI Performance Patterns for React Native

The Margelo team published a deep breakdown of how architectural choices affect JSI performance. The numbers are striking: a plain HostFunction call runs about 5x faster than the same function exposed through a HostObject, because HostObject::get() runs on every property access and involves virtual dispatch, string conversion, and string comparison on each call.

Using jsi::NativeState instead of HostObject for stateful native objects is another 5x improvement. And on the string side, stack-allocated char buf[256] buffers are about 3x faster than std::string for strings beyond the SSO threshold, with additional gains from using createFromAscii() when you know the data is ASCII-only.

The higher-level point is the right one: optimize the API shape before the micro-level memory layout. Fewer JS-to-C++ boundary crossings matter more than any individual call optimization. Worth bookmarking if you're building anything in the native module layer.

Part 1: How to Make Pure JSI Code Faster in React Native

React Native Swift Package Manager RFC

The React Native community has an RFC open to replace CocoaPods with Swift Package Manager for iOS dependency management. CocoaPods has been the standard for over a decade, but it's showing age: slow installs, Ruby dependency chain, fragile lockfiles. Apple has been pushing SPM hard and has signaled that CocoaPods support in Xcode tooling isn't getting much love going forward.

The RFC proposes a migration path for library authors and app developers. This is a multi-year transition, not a flip of a switch, but the direction is set. If you maintain a React Native library with native iOS code, now is a good time to start thinking about your SPM story.

RFC: Swift Package Manager in React Native

pnpm 11.0

Big release. The highlights: Node.js 22+ is now required, and pnpm itself is pure ESM. The store index is rebuilt around SQLite instead of millions of JSON files, which means fewer syscalls and faster installs. Global installs are now properly isolated, each getting its own directory with its own package.json and lockfile, so peer dependency conflicts between global packages stop interfering with each other.

Supply-chain security defaults tightened: minimumReleaseAge defaults to 1440 minutes (one day), so newly published packages don't resolve immediately. blockExoticSubdeps is on by default.

The .npmrc change is the one that will bite people: pnpm no longer reads non-auth settings from .npmrc. Configuration moved to pnpm-workspace.yaml. The codemod handles most of it, but you need to actually run it before upgrading CI. The new pnpm ci command (clean install with frozen lockfile) and pnpm sbom for generating software bills of materials are both useful additions.

pnpm 11.0

Fresh 2.3: Zero JS Actually Means Zero JS Now

Fresh, the Deno-based web framework built on Preact, shipped 2.3 with a fix that was overdue. The "zero JavaScript by default" tagline was technically true in intent but not in practice, because every page got a small bootstrap script even when it had no islands or partials. That's fixed now. Pages with no interactive islands ship zero script tags, zero module preloads, nothing. Down from 14-22 KB to 0 KB.

View Transitions are now wired into the partials system with a single attribute on the body tag: f-view-transition. First-class WebSocket support landed too, with a clean app.ws() API and a managed mode that handles the upgrade response automatically. The middleware chain pre-compilation (built once at startup instead of per-request) is the kind of optimization that's boring to read about and very nice to have in production.

Fresh 2.3: Zero JS by default, View Transitions, and Temporal support

Node.js 26.0.0

Node 26 shipped April 28th. The standout: Temporal is enabled by default. That's the new date/time API that replaces the deeply flawed Date object, and it's been a long time coming. V8 updated to 14.6, undici updated to 8.0.2 for HTTP.

Some things that go away: --experimental-transform-types is removed (so TypeScript enum syntax at runtime is gone), module.register() is runtime-deprecated, and the _stream_* APIs move to end-of-life. The extensionless CJS exception for type: module packages is also gone. If you've been relying on that edge case, now's the time to fix your file extensions.

Node 18, 19, 20, and 21 are no longer supported by the Node team. Plan your upgrade accordingly.

Node.js 26.0.0

CSS Scroll-Driven Animations

Josh Comeau published a thorough tutorial on the Animation Timeline API. The core concept is simple once you see it: animation-timeline: view() maps an element's progress through the viewport to the keyframe percentage, instead of mapping to time. So 0% is "element enters viewport" and 100% is "element exits viewport."

The animation-range property controls which part of the viewport journey triggers the animation. entry fires during entry only. exit fires on the way out. contain starts only once the element is fully visible. And the named timeline system (view-timeline, timeline-scope) lets you drive one element's animation from another element's scroll position, which opens up sticky sidebar effects and other patterns that previously required JavaScript.

Browser support is solid now. This is native CSS with no JS. Worth learning.

Scroll-Driven Animations

Key Takeaways

  • The React Compiler is being ported to Rust with all 1724 fixtures passing. Greenfield React performance optimization is increasingly automated.
  • Base UI is the new default primitive layer for shadcn/ui. The asChild pattern is out, render props are in, and everything ships from one package.
  • TSRX offers stateful, cut-pasteable component blocks as an alternative to JSX, with signal-library semantics baked into the syntax.
  • pnpm 11 requires Node 22, moves config out of .npmrc, and ships a SQLite store. Run the codemod before upgrading.
  • Node.js 26 enables Temporal by default and removes --experimental-transform-types. TypeScript enum runtime support is gone.
  • Fresh 2.3 finally delivers true zero JS for static pages, plus View Transitions and WebSockets built in.
  • CSS Animation Timeline API is ready to use and replaces most scroll-animation JavaScript with pure CSS.
  • JSI performance in React Native comes down to architecture first: HostFunction over HostObject, NativeState over HostObject for stateful objects.