React Compiler Goes Everywhere, Babel 8 Finally Ships, and React Native 0.86 Lands Clean
Published on 18.06.2026
React 19 Upgrade Temporarily Reverted in Gutenberg
TLDR: WordPress's Gutenberg editor upgraded to React 19, discovered widespread plugin incompatibilities almost immediately, and reverted back to React 18 within days. The core team is now rethinking the migration strategy before attempting it again in WordPress 7.1.
Summary: This one stings a bit, and honestly, it was somewhat predictable. The WordPress team announced they were upgrading Gutenberg to React 19, shipped it in version 23.3.0, and then had to roll it back in 23.3.2 because a huge number of third-party plugins started crashing. The culprit was not an API change, because React 18 and 19 have virtually no API differences at that level. The problem was the runtime itself. Many plugins bundle their own copy of the react/jsx-runtime helper for processing JSX syntax, and the shape of the generated element objects changed between versions. React 19 actively checks and rejects elements generated by a React 18 runtime. That is a hard failure mode, not a soft degradation.
What this exposes is the fundamental tension in an ecosystem as large as WordPress, where thousands of independently maintained plugins exist and the upgrade path is not something you can coordinate across the board. The core team now needs to engineer a feature-flag-driven approach, along with a compatibility layer for already-published plugins, before they can attempt the move again.
I find the reasoning here a little too optimistic in retrospect. "Virtually no API changes" turned out to mean nothing when the runtime boundary itself changed. The lesson is that bundled dependencies are a time bomb in ecosystems like this, and no amount of "no breaking changes" messaging covers for that. The plan to use a feature flag and a compat layer is the right call, but it should have been the plan from day one.
Key takeaways:
- React 19's runtime actively rejects JSX elements generated by React 18's runtime, breaking plugins that bundle their own react/jsx-runtime
- The WordPress team is committing to React 19 in WordPress 7.1 with a more incremental, flag-based strategy
- Large plugin ecosystems with bundled dependencies require migration compatibility layers, not just API-compatibility guarantees
Why do I care: This matters for anyone maintaining a large component ecosystem or design system where third-party consumers bundle framework code. The failure mode here, where runtime-level incompatibility breaks consumers even when the public API is stable, is a pattern that will repeat. If you maintain a library that gets embedded broadly, you need to think about runtime isolation, not just API surface. WordPress's situation is extreme, but the lesson scales down.
React 19 upgrade temporarily reverted in Gutenberg
TSRX: Rethinking the TypeScript Language Extension for Declarative UI
TLDR: TSRX, the TypeScript language extension for declarative UI, has pivoted back toward JSX compatibility and introduced the @ prefix syntax to clearly distinguish template control flow from plain JavaScript. The project is now entering beta with no further core syntax changes planned.
Summary: TSRX is a fascinating project, and this update represents a significant course correction based on real user feedback. The core insight that drove this change was that earlier versions of TSRX used syntax that looked too much like native JavaScript. Control flow constructs like if, switch, try, and for inside components looked like regular JavaScript but had different semantics, which confused both human readers and AI coding assistants trying to edit the code. That is a legitimate problem.
The fix is elegant. TSRX now uses an @ prefix for all template control flow, so @if, @for, @switch, and @try are visually distinct from their JavaScript counterparts. The language also introduces statement containers with @{...} syntax, which lets you write imperative setup code followed by a single JSX output node. It is expression-based now, meaning it composes naturally with variable assignments and return statements, which makes the whole thing far more predictable. Static text in JSX no longer requires extra quoting. Comments work as expected. The boundary between JavaScript and template syntax is now visible rather than inferred.
One thing I appreciate about this design is the explicit decision around React hooks. TSRX does not try to automatically move conditional hook calls into generated child components. It requires you to make hook-owning components explicit. That is the right call. Hiding hook state in generated children would be a source of bugs that are genuinely hard to debug.
The bet being made here is that as AI agents write more of our UI code, syntax that is self-describing and clearly delimited will produce better results than syntax that looks familiar to JavaScript training data. That is a plausible hypothesis, and I am curious to see if it holds up in practice.
Key takeaways:
- The @ prefix now marks all TSRX template control flow, making the boundary between JavaScript logic and UI output unambiguous
- Statement containers using @{...} allow imperative setup followed by a single JSX output, replacing the need for wrapping fragments
- TSRX is entering beta with stable core syntax, shifting focus to compiler hardening, editor tooling, and documentation
Why do I care: Whether or not TSRX becomes widely adopted, the design problems it is solving are real. The challenge of making template syntax legible to both humans and AI tools is going to matter more as automated code generation becomes the norm. For teams building internal DSLs or template abstractions on top of React, the tradeoffs explored here are worth studying.
TSRX | TypeScript Language Extension for Declarative UI
TanStack Start: A Mental Model for Next.js Developers
TLDR: This article walks Next.js developers through TanStack Start's router-first architecture, explaining how its explicit server boundaries, typed routing, and server functions differ in philosophy from Next.js's file-based conventions.
Summary: Moving from Next.js to TanStack Start is not just a framework swap. It requires a different way of thinking about where the boundaries between client and server actually live. Next.js bakes a lot of those decisions into the file system: app/ versus pages/, Server Components as the default, route handlers in route.ts files. TanStack Start takes a more explicit, code-first approach where you declare server functions with a marker, wire up loaders and actions with typed signatures, and the router is the central organizing concept rather than the file tree.
What this post gets right is framing TanStack Start's approach not as better or worse, but as a different mental model that emphasizes explicitness over convention. If you come from Next.js, the lack of a file-system router feels unfamiliar, but the upside is that the type safety from route parameters through to loader data to component props is genuinely end-to-end. The server function model, where you write a function and call it from the client without thinking about API endpoints, is also cleaner than Next.js's pattern of creating route.ts files and then fetching from them.
For anyone considering TanStack Start seriously, the main thing to internalize is that the router drives everything. Data fetching, code splitting, server-side logic, all of it flows from the router definition. That is a different kind of discipline than Next.js's approach, but for complex applications where type safety across the server-client boundary actually matters, the tradeoffs start to look attractive.
Key takeaways:
- TanStack Start centers the router as the primary organizing primitive, while Next.js centers the file system
- Server functions in TanStack Start are explicit TypeScript functions rather than implicit route handlers, giving full type safety across the server-client boundary
- Developers coming from Next.js need to reframe their mental model around typed routing and explicit server boundaries rather than file-based conventions
Why do I care: TanStack Start is gaining real traction, and as a Next.js developer it is easy to dismiss it as just another framework. But the end-to-end type safety story is genuinely better, and for teams building complex data-heavy applications, that matters. Worth understanding the mental model shift before writing it off.
TanStack Start: A Mental Model for Next.js Developers
Module System Dependency Injection in React and Friends
TLDR: This article examines the tradeoffs between runtime dependency injection via React Context and compile-time DI via Node's module system subpath imports, concluding that the module system works well as a static composition root but should not replace the ability to pass dependencies explicitly.
Summary: Jay Freestone digs into a pattern that has been gaining traction in the Next.js community: using Node.js conditional exports and subpath imports as a form of dependency injection. The idea is straightforward. Instead of passing an analytics client through React Context or function arguments, you configure the module system so that import from "#analytics" resolves to different implementations depending on build conditions. Set a Node option flag and you get the production client. Set a different flag and you get the no-op version.
The appeal is real. You get zero runtime overhead, the implementation swap happens at module resolution time, and in frameworks like Next.js where the entry points for server-side code are abstracted away by the bundler, it is sometimes the cleanest available option. Turbopack has its own module resolution setup that does not respect native import conditions directly, which means Next.js users need to add a configuration shim, but it is manageable.
The limitation Freestone identifies is important though. Compile-time DI means you lose the ability to override dependencies inline in tests. You cannot pass a stub object into a module that resolves its dependencies at build time. You end up needing module-level mocking, which he rightly describes as being against best practices. The recommended approach is to treat the module system as a static composition root for infrastructure concerns like analytics or feature flags, while keeping business logic behind function factories and interfaces that accept dependencies as arguments.
What the article does not fully explore is the interaction with server components, where React Context is not available and the module system pattern becomes more attractive by default. That is the space where this tradeoff gets most interesting in practice.
Key takeaways:
- Node.js subpath imports enable compile-time dependency injection by resolving different implementations based on build conditions, with no runtime overhead
- The tradeoff is losing the ability to pass inline test doubles, which forces reliance on module-level mocking
- The recommended approach treats module system DI as a static composition root for infrastructure, while business logic remains testable through explicit dependency passing
Why do I care: Anyone building large Next.js or React applications eventually runs into the DI question. This article is one of the cleaner treatments of the module system approach I have seen, and the conclusion, use it for infrastructure, not for everything, is the right one. If you are using feature flags or analytics clients that need to swap between environments, this pattern is worth knowing.
Module System Dependency Injection in React & Friends
Building an LLM-Safe Design System
TLDR: Polar's engineering team built Orbit, a design system using StyleX and a typed Box component that constrains all styling to predefined design tokens, with ESLint rules enforced in CI to prevent LLMs from drifting off-system during code generation.
Summary: This is one of the most practical and honest pieces about AI-assisted frontend development that I have read in a while. The team at Polar starts from a simple observation: LLMs write CSS fluently, but without awareness of your design system's decisions. Ask an AI agent to build a card and it will reach for reasonable-looking Tailwind classes that are not yours. Multiply that across hundreds of generated components and your interface drifts into a thousand slightly different grays, none of which match your actual design decisions.
The solution they landed on is architectural. They built Orbit around StyleX, Meta's compile-time styling library, and a single polymorphic Box component whose props accept design tokens rather than arbitrary values. The tokens are named for intent rather than value: background-card is a decision, bg-gray-100 is a value. That distinction matters because it gives the LLM a closed vocabulary of design decisions rather than an open space of CSS values.
The second half of the solution is enforcement. They ban raw div elements via ESLint, replacing them with Box which uses an as prop for semantic HTML elements. And crucially, all of this is enforced in CI, not in documentation. The insight here is direct: anything you put in a CLAUDE.md or style guide is a probability. CI is a contract. If the check is green, it is safe to merge. That is a fundamentally different guarantee.
I think this is exactly right. The mental shift from "document your conventions" to "encode your conventions as machine-checkable rules" is the one that changes everything when AI agents are writing most of your code. The dark mode handling is particularly clever: each token carries both its light and dark value using the native CSS light-dark() function, so there is no separate dark mode pass for an LLM to forget.
Key takeaways:
- Design tokens named for intent rather than value constrain LLMs to a closed vocabulary of design decisions, preventing off-system style drift
- Banning raw HTML layout elements in favor of a typed Box component removes the unconstrained path that LLMs default to from training data
- ESLint rules enforced in CI provide a deterministic contract that survives an LLM's fresh context window, unlike documentation
Why do I care: If you have LLMs writing any significant portion of your UI code, this article is required reading. The specific implementation, StyleX plus a Box component, is one approach, but the principle applies regardless of your styling system. Encode your conventions as type constraints and lint rules, not as prose instructions. This is the direction the industry needs to move.
Building an LLM safe design system
React Native 0.86: Edge-to-Edge and DevTools Improvements
TLDR: React Native 0.86 ships comprehensive edge-to-edge support fixes for Android 15+ and DevTools improvements including light/dark mode emulation, with no user-facing breaking changes for the second release in a row.
Summary: React Native 0.86 is a solid incremental release, and the "no breaking changes" streak continuing for a second release is genuinely good news for teams that have been burned by upgrade friction in the past. The headline feature is comprehensive edge-to-edge support on Android 15+. This is more consequential than it sounds because Android 15 enforces edge-to-edge mode at the OS level for new apps, and the previous React Native behavior had a handful of bugs that made this painful. measureInWindow now returns correct coordinates, KeyboardAvoidingView works properly, and modal visibility status bar changes work as expected.
Also notable is the repository move. React Native, React, Metro, Yoga, and the React Native website have all moved from the facebook GitHub organization to the react organization as part of the transition to the React Foundation. GitHub redirects existing URLs automatically, so nothing breaks, but this is a symbolic and organizational step worth acknowledging.
The JSI improvements are interesting for library authors. New APIs include first-class TypedArray support, the ability to append to arrays, string length without full conversion, and factory methods for all standard JS error types. These are low-level capabilities that Nitro module authors in particular will find useful.
There are also some web spec alignment improvements worth noting. PerformanceObserver now correctly defaults to a 104ms duration threshold for event timing, matching the W3C spec. These small alignment improvements compound over time and matter for portability between React Native and web.
Key takeaways:
- Android 15+ edge-to-edge mode is now comprehensively supported, fixing layout coordinate and keyboard avoidance bugs
- React Native's GitHub repositories have moved to the react organization as part of the React Foundation transition
- New JSI APIs add TypedArray support, array mutation, and standard error type factories for native module authors
Why do I care: Zero breaking changes twice in a row signals that the React Native team is genuinely prioritizing upgrade ergonomics. For teams that have been stuck on older versions because upgrades were too painful, 0.86 is a good release to target. The edge-to-edge fixes alone are meaningful for any app targeting Android 15+ devices.
React Native 0.86 - Edge-to-Edge and DevTools Improvements, no breaking changes
TanStack AI Hits Beta: Provider-Agnostic AI Tooling Matures
TLDR: TanStack AI has reached beta with stable core APIs, multi-modal support across text, audio, image, video, and realtime voice, and a provider-agnostic architecture built on the AG-UI protocol, backed by 265 end-to-end tests across 10 LLM providers.
Summary: TanStack AI shipped its alpha in December with a promise of no vendor lock-in and no framework dictating your stack. Six months later, the beta delivers on that premise in a way that is hard to ignore. What started as a handful of text adapters is now a full multi-modal platform. Every major modality gets a first-class typed activity, and every provider ships small capability-split adapters rather than one monolithic package. You get openaiText and geminiAudio as separate imports rather than a god-object that does everything.
The hook family is comprehensive. useChat, useGeneration, useGenerateImage, useGenerateAudio, useGenerateSpeech, useTranscription, useSummarize, useGenerateVideo, and useRealtimeChat all follow the same shape and wire into devtools automatically. The TypeScript story is thorough too: model options are typed per-model, so your IDE knows what each model actually supports rather than letting incompatible tool pairings fail silently in production.
The architecture choices are interesting. AG-UI events are the wire format, which means TanStack AI backends can be written in any language that speaks the protocol. LangGraph, CrewAI, Mastra, Pydantic AI running in Python can all sit behind a TanStack AI frontend. That is a genuine differentiator compared to the Vercel AI SDK's closer coupling to the Vercel platform.
The 265 deterministic end-to-end tests across 10 providers is a real commitment. Provider APIs change, models get deprecated, and having a test suite that catches it before you do is the kind of infrastructure investment that separates production-grade tooling from prototypes.
Key takeaways:
- TanStack AI beta covers text, streaming structured data, tool calls, image, video, audio, and realtime voice through a unified provider-agnostic API
- The AG-UI protocol as the wire format enables interoperability with agent frameworks in any language, not just TypeScript
- Per-model TypeScript types gate provider-native tool capabilities at the type level, catching incompatibilities at compile time rather than runtime
Why do I care: The Vercel AI SDK has a head start and tight Next.js integration, but TanStack AI's provider neutrality and multi-framework support are serious advantages for teams not fully committed to the Vercel platform. The AG-UI protocol interoperability is particularly interesting if you are running Python-based agents. This is worth evaluating seriously.
TanStack AI Beta: The Switzerland of AI Tooling Grows Up
Four Years of React Native Quick Crypto: From Wallets to Node Parity
TLDR: React Native Quick Crypto has grown from a wallet-focused crypto library to a near-complete Node.js crypto module replacement on mobile, now supporting post-quantum algorithms, a full security audit, and 244,000 weekly downloads, up fivefold in twelve months.
Summary: This is a genuinely impressive engineering retrospective. React Native Quick Crypto started in February 2022 to solve a specific problem: pure-JavaScript cryptography implementations were too slow for wallet apps doing PBKDF2, HMAC, and SHA operations. Marc Rousavy's initial JSI-based implementation linked OpenSSL directly, bypassed the bridge, and immediately delivered meaningful speedups.
Four years later the scope has expanded dramatically. The library now covers most of the WebCrypto subtle API, Ed25519 and X25519 for modern key exchange, ChaCha20-Poly1305, SHA-3 and its variants, and the NIST post-quantum finalists ML-KEM, ML-DSA, and SLH-DSA through OpenSSL 3.6. That last part is not trivial. Post-quantum signatures are large, ML-DSA-87 signatures are 4,627 bytes, and any pure-JavaScript implementation would be unusable on mobile. Having OpenSSL's hand-tuned C on the device changes the calculus entirely.
The Nitro Modules rewrite in 2024 replaced hand-written JSI plumbing with generated bindings, which reduced bugs and made adding new algorithms significantly cheaper. The benchmark numbers are striking: HMAC SHA-256 on an 8 MB buffer runs 1,418 times faster than the nearest JavaScript alternative. DH modp14 key generation runs 7,480 times faster. These are not rounding errors.
The six-phase security audit in April 2026 caught real issues, including a two-time-pad vulnerability in the streaming XSalsa20 implementation and a Bleichenbacher oracle in RSA PKCS#1 v1.5 decryption. The fact that these were caught and fixed before widespread production use is the audit doing its job.
Key takeaways:
- React Native Quick Crypto now provides near-complete Node.js crypto module parity on mobile, including post-quantum algorithms via OpenSSL 3.6
- The Nitro Modules rewrite turned hand-written JSI plumbing into generated bindings, reducing technical debt and making algorithm additions substantially cheaper
- A six-phase security audit caught critical vulnerabilities including a two-time-pad and a Bleichenbacher oracle, both now fixed in the 1.1.x line
Why do I care: If your React Native app touches cryptography for anything beyond a toy use case, whether for wallets, local-first data sync, secure messaging, or authentication, this library is the correct choice. The Node parity goal means you can share code with your Node.js backend, the performance advantage over pure-JS is large enough to matter in practice, and the security audit gives you more confidence than most React Native libraries can offer.
Four Years of React Native Quick Crypto: From Wallets to Node Parity
React Native, Hermes Bytecode, and the Kindle Homepage
TLDR: An in-depth technical exploration of how Amazon's Kindle uses React Native with Hermes bytecode for its homepage app, and how the open-source KPP_Patch project patches that bytecode to remove ads and modify the UI.
Summary: This is a deeply technical piece that doubles as an accidental testament to how far React Native has spread. Since firmware 5.14.2 in 2022, Amazon's Kindle uses React Native for its homepage. Not a small feature, the entire homepage. And because Hermes compiles JavaScript to a bytecode format rather than shipping raw JS, patching it requires understanding Hermes's internal instruction set.
The piece walks through how Hermes bytecode is structured, how you find specific functions within the compiled output, and how KPP_Patch modifies those functions to remove sponsored content from the Kindle lock screen. The author contributed to this project and explains the disassembly and patching process in technical detail.
What I find interesting here beyond the Kindle hacking angle is what it reveals about React Native in production. Amazon chose React Native for the Kindle homepage because it provided a fast development cycle and cross-platform logic reuse. The Hermes compilation step, which produces bytecode rather than JavaScript, is a production optimization that is genuinely different from what most developers encounter in their day-to-day work. The bytecode runs on a constrained ARM device, and the engineering tradeoffs involved in making that work are not trivial.
This is also a good reminder that React Native runs in some unexpected places. Kindle is the most unlikely deployment I have heard of, but it works, and the engineering is apparently solid enough that Amazon has been shipping it in production for four years.
Key takeaways:
- Amazon's Kindle homepage has been running on React Native with Hermes bytecode compilation since 2022 firmware
- Hermes compiles JavaScript to a compact bytecode format optimized for constrained devices, which is what enables React Native to run on ARM hardware like a Kindle
- The KPP_Patch project demonstrates that React Native's Hermes bytecode is patchable, with the same disassembly techniques used for Java bytecode patching in earlier Kindle firmwares
Why do I care: Knowing that React Native runs on Kindles is both amusing and instructive. It validates that the Hermes engine's constrained-device story is real, not just marketing. For anyone targeting low-memory or low-power devices with React Native, this is evidence that the architecture scales down as well as up.
React Native, Hermes bytecode, and the Kindle homepage
AI-Assisted React Native Migration for TV: Lessons From Zattoo
TLDR: Zattoo migrated their multi-platform streaming app to React Native, driven by Amazon's Vega OS adoption, and used shared architecture to reduce organizational duplication across teams while keeping platform-specific code explicit rather than hidden.
Summary: Zattoo's migration story is a useful corrective to the "write once, run everywhere" fantasy that sometimes accompanies React Native adoption. They started from a place most large streaming companies know well: separate native codebases for Android TV, Apple TV, Fire TV, web, and desktop, each with its own team and its own implementation of the same product behavior. The pause button story in the article is a good example of the real cost. A single user-facing control required coordination across multiple backend and frontend teams to make consistent, because the implementation existed separately in each codebase.
The push to React Native came from Amazon's new Vega OS, which positioned React Native as the primary development path for Fire TV apps. Rather than building yet another dedicated native app, Zattoo investigated whether this could be the start of a broader shared platform. The answer was yes, but only if the team accepted that "shared" does not mean identical.
The architecture principle they landed on is worth repeating: share implementation until a platform gives you a real reason to split it. Playback, SSO, OS content integrations, and input handling stayed platform-specific because native capabilities genuinely mattered there. TV guide, content details, search, and shared business logic were good candidates for sharing. Performance still required telemetry from real devices across more than 800 Android TV hardware variants.
The organizational changes mattered as much as the technical ones. Moving from platform-focused teams to value-stream teams organized around product areas like playback experience and content search was only possible because the shared codebase reduced the cognitive load of owning a full platform stack.
Key takeaways:
- Sharing implementation should be a deliberate decision made flow by flow, not a blanket goal, keeping platform-specific behavior explicit rather than scattered as conditionals
- Organizational structure follows architecture: shared React Native code enabled product-area teams instead of platform-siloed teams, reducing duplication of product decisions
- Performance, playback, and release cadence still require platform-specific handling even with a shared codebase, and telemetry from real devices remains essential
Why do I care: This is the most grounded account of a large-scale React Native TV migration I have read. Most case studies either oversell the shared code benefits or undersell the platform-specific work that remains. Zattoo's honesty about where sharing actually helped versus where native expertise was still needed makes this directly applicable to planning similar migrations.
AI-Assisted React Native Migration for TV: Lessons From Zattoo
Releasing Babel 8: ESM-Only, Drop ES5 Default, and a Smooth Migration Path
TLDR: Babel 8 has shipped after eight years, dropping ES5 as the default compilation target, switching to ESM-only distribution, adding TypeScript types for all packages, and targeting Node.js 22 and above, with no new features over Babel 7 but modernized infrastructure throughout.
Summary: Eight years is a long time between major versions, and Babel's team deserves credit for taking an approach that prioritizes migration smoothness over dramatic feature announcements. Babel 8 has no new features over Babel 7. That is a deliberate choice. The release is entirely about modernizing the foundation: ESM-only packages, TypeScript types baked in instead of requiring separate @types packages, and defaulting to targeting evergreen browsers instead of ES5.
The ES5 default change is the one that will affect the most people in practice. Previously, if you did not configure targets explicitly, Babel would compile everything down to ES5. That default made sense in 2018. It does not make sense now that IE11 is gone and every browser in meaningful use supports ES2023 and above. The new default follows Browserslist's defaults query, which means your output automatically improves as the browser market evolves without you doing anything. Bundle sizes will shrink as a side effect.
The ESM-only shift requires Node.js 22 or newer, which is a meaningful constraint for some teams. The good news is that require(esm) support is now available in all LTS Node.js versions, so CommonJS users are not left stranded in their own code. The Babel packages themselves are ESM, but they can be consumed from CommonJS code through Node's interop layer.
The funding situation mentioned at the end of the post deserves attention. Babel has grown to 651 million weekly downloads while its core team of three sees declining donations. The Sovereign Tech Agency funding that sustained the Babel 8 push is ending. This is a real sustainability problem for critical infrastructure.
Key takeaways:
- Babel 8 defaults to targeting evergreen browsers via Browserslist rather than ES5, reducing bundle sizes automatically without configuration changes
- All packages are now ESM-only and ship TypeScript types, eliminating the need for separate @types/babel__ packages
- Babel 7 receives security support until June 2027, after which backporting depends on community request volume
Why do I care: Most React projects run Babel somewhere in their pipeline, even if they have partially migrated to SWC or esbuild. The ES5 default change alone is worth upgrading for, since the resulting bundle size reduction is essentially free. Check the migration guide before upgrading, but this is a release you want to be on sooner rather than later.
Releasing Babel 8 today: ESM-only, drop ES5 default, and a smooth migration path
Biome v2.5: 500 Lint Rules, Plugin Code Fixes, and Cross-File Linting
TLDR: Biome v2.5 crosses 500 lint rules, introduces cross-file CSS class analysis to catch undefined and unused classes in JSX, ships plugin code fix support, adds a file watcher for real-time linting, and includes a 13% performance improvement across the board.
Summary: Biome continues its aggressive expansion, and 500 rules is a number that demands attention for anyone evaluating it as an ESLint replacement. But the number is not the interesting part of this release. The interesting part is the cross-file analysis capabilities enabled by the module graph.
The noUndeclaredClasses and noUnusedClasses rules can analyze CSS class usage across the entire project. If your JSX references a class that does not exist in any imported stylesheet, you get a diagnostic with the full import tree showing exactly how the file was reached. If you define a class that nothing references anywhere, you hear about it. This is the kind of lint rule that catches real bugs, the kind where you rename a CSS class, forget to update the JSX, and wonder why the styling disappeared in production.
Plugin code fixes are another meaningful addition. Previously plugins could only report diagnostics. Now they can propose fixes, marked as safe or unsafe, that apply through the standard Biome CLI write commands. Combined with GritQL, this makes Biome plugins considerably more useful for automated codebase transformations.
The --watch mode for lint, format, and check commands is a quality-of-life improvement for teams not using the VS Code extension. And the concise reporter is a thoughtful addition for AI coding agents: shorter output means fewer tokens consumed when interpreting diagnostics.
The Vue framework rules getting promoted from nursery to stable is also notable. Biome is clearly positioning itself as the single tool for JavaScript, TypeScript, and increasingly Vue projects.
Key takeaways:
- Cross-file CSS class analysis catches undefined and unused classes in JSX with full import-tree diagnostics, addressing a common source of styling bugs
- GritQL plugins can now declare code fixes with safe or unsafe classification, enabling automated codebase transformations through the standard CLI
- A 13% performance improvement and new --watch mode make Biome a more compelling real-time development tool
Why do I care: Biome is the most serious challenger to the ESLint and Prettier combination that has dominated frontend tooling for years. The cross-file CSS analysis puts it ahead of anything ESLint can do for CSS-in-JSX patterns, and the Vue support expansion makes it viable for multi-framework monorepos. If you have not evaluated Biome recently, v2.5 is a good reason to revisit.
Biome v2.5—500 Lint Rules, Plugin Code Fix, and Cross-File Linting
TypeScript Performance in TanStack Table V9
TLDR: TanStack Table V9 reduced TypeScript instantiations by 62 to 86 percent between alpha and beta through feature maps, interface restructuring, variance annotations, and explicit type arguments, bringing type-checking cost to about twice V8's despite V9 offering dramatically more type capability.
Summary: This is one of the best deep-dives into TypeScript performance optimization I have seen from a library author, and the lessons extend well beyond TanStack Table. The core problem was that V9's modular feature system, where you pass exactly the features you want and both runtime and types are assembled from your selection, required computing the table type dynamically from a union of feature types. In the alpha, this was done through fourteen hand-written conditional branches fed into UnionToIntersection. Every internal function evaluation triggered the whole computation, and it was not cacheable because each call site had different generic arguments.
The solution to the first problem was moving the feature-to-type mapping into a named interface. A named type only costs instantiations when the compiler first resolves it, and subsequent lookups hit the cache. The hand-written conditionals became a single index into the feature map interface. That alone cut instantiations dramatically.
The variance annotation story is particularly instructive. When a type parameter flows through conditional types, the TypeScript compiler cannot determine its variance reliably, so it falls back to structural comparison. Adding explicit in out annotations tells the compiler the parameter is invariant, which lets it compare instantiations by their type arguments directly without structural expansion. This was the difference between the react adapter getting worse and getting better when Table_Internal became an interface.
The explicit type arguments lesson is one that applies immediately to anyone writing construction helpers in TypeScript. When you spread an object into a generic function without explicit type arguments, the compiler has to infer the type parameters from the object's shape. That inference is expensive. Passing the type arguments explicitly replaces inference with a cheap assignability check.
Key takeaways:
- Named interface feature maps replace per-call-site conditional type unions, creating a cache point the compiler can reuse across all callsites
- Variance annotations on invariant type parameters prevent the compiler from falling back to expensive structural comparison when type parameters flow through conditionals
- Explicit type arguments in construction helpers eliminate type inference overhead, replacing it with a simple assignability check
Why do I care: If you maintain any TypeScript library with complex generics, this article should be required reading. The tsc --extendedDiagnostics and --generateTrace approach gives you hard numbers to optimize against rather than vibes. The variance annotation trick in particular is underused and has high leverage when your type parameters touch conditional types.
TypeScript Performance in TanStack Table V9
Zod Compiler: Build-Time Schema Compilation for 2-75x Faster Validation
TLDR: zod-compiler is a Vite, webpack, esbuild, and Rollup plugin that compiles exported Zod schemas into optimized validation functions at build time, delivering 2 to 75 times faster validation with no source code changes required in automatic mode.
Summary: The premise of zod-compiler is straightforward: Zod schemas are declarative data structures, so a build-time plugin can analyze them and generate optimized validation code that skips Zod's runtime traversal entirely. The result is a two-phase validator where valid input takes a fast path through a single boolean expression chain with zero allocations, and invalid input only triggers the error-collecting slow path when you actually read the error.
The benchmark numbers are real. Medium objects validate 4 times faster, large 100-key objects validate 73 times faster, and invalid input in safeParse is 194 times faster than Zod v4 because the error walk is deferred until you read the error property. The worst case improvement is 1.1 times on a simple string schema, which is essentially noise.
The automatic mode is the compelling part for adoption. You add the plugin to your Vite config and it scans your builds for exported Zod schemas without requiring any changes to your schema files. The full Zod API is preserved on the compiled schema objects, including the shape property, Standard Schema compliance, and compatibility with tRPC, Hono, and React Hook Form. The compiled schema is the original Zod schema with optimized parse methods installed directly on it.
Schema hoisting is a useful bonus feature. If you define a Zod schema inside a function, it gets rebuilt on every call. The plugin moves those schema definitions to module scope at build time, so they are constructed once. This is a common pattern in React components that create schemas inline, and the performance difference is significant.
The caveats are real: unknown keys are not stripped by default, which differs from Zod's behavior, and any schema with captured transform callbacks falls back to runtime Zod for that field. The diagnostic CLI command that shows compilation coverage and Fast Path eligibility is a thoughtful addition.
Key takeaways:
- Build-time schema compilation generates zero-allocation validation functions, delivering 2-75x faster validation with no source code changes in automatic mode
- The full Zod API is preserved on compiled schema objects, maintaining compatibility with tRPC, React Hook Form, and Standard Schema consumers
- Schema hoisting moves inline schema definitions from function bodies to module scope, eliminating per-call reconstruction overhead
Why do I care: Schema validation is on the hot path in virtually every web application, whether for API request parsing, form validation, or data transformation. A 4 to 73 times improvement on object validation with zero code changes is the kind of drop-in win that is hard to ignore. The automatic mode's no-source-changes requirement removes the usual adoption barrier for performance tooling.
App.js Conf 2026 Recap: Gesture Handler 3.0 and More
TLDR: App.js Conf 2026 brought major announcements from Software Mansion including Gesture Handler 3.0 with a hook-based API, a new bottom sheet library, React Native Screens 5.0 announcement dropping legacy architecture, and the TypeGPU CLI enabling GPU shader programming in TypeScript.
Summary: App.js Conf is where the React Native community announces the things that will shape the next year of development, and 2026's edition delivered. The biggest announcement was Gesture Handler 3.0, which fundamentally changes how gestures are managed in React Native. The previous architecture tied gesture lifecycle to the component it was attached to. The new hook-based API separates gesture management from rendering: gestures live in hooks, components just attach them to views. SharedValues can now feed directly into gesture configuration, which means gesture properties can change without triggering re-renders. Press feedback and ripple effects run on the platform side, eliminating the JS round-trips that used to slow them down.
The architectural direction here reflects what the New Architecture enables. When native and JavaScript can communicate synchronously through JSI, you can move more logic to the native side without the responsiveness penalties that made the bridge era feel sluggish. Gesture Handler 3.0 is arguably the library that benefits most visibly from this shift.
The TypeGPU CLI announcement is worth paying attention to even if you are not doing GPU programming today. The underlying problem, that GPU shader programming has been stuck in the equivalent of pre-bundler JavaScript with global state and string concatenation, is real. TypeGPU brings dead-code elimination, generics, and compile-time specialization to shader code, and the same TypeScript shader can target Three.js, React Native Skia, and other WebGPU environments. One command setup is the right onboarding experience.
React Native Screens 5.0 dropping legacy architecture support is the expected next step as the ecosystem continues moving forward. The reworked stack and tabs implementation should make the new version more capable for apps already on the New Architecture.
Key takeaways:
- Gesture Handler 3.0 moves to a hook-based API where gestures are managed in hooks and components only handle attachment, with platform-side press feedback eliminating JS round-trips
- Software Mansion shipped a new bottom sheet library built natively on Gesture Handler and Reanimated, solving the scroll-gesture conflict at the right abstraction level
- React Native Screens 5.0 will drop legacy architecture support and ship a reworked stack and tabs implementation targeting New Architecture users
Why do I care: Gesture Handler 3.0 will need migration work, but the hook-based model is the right design. The cognitive overhead of reasoning about gesture lifecycle tied to component lifecycle was a real source of bugs. If you are building touch-heavy interfaces in React Native, planning the migration now rather than when 3.0 becomes the minimum requirement is the smart move.