Is React Cooked?
Let’s talk about Remix v3. The tldr; is that Remix v3 will not be based on React. See Wake up, Remix! to learn their truth; stick around here to hear my hot takes on what the wider implications are for the frontend space.
This news shouldn't come as a complete shock to those who've been closely observing the evolving landscape of web frameworks. Remix's roots are deeply intertwined with React, and they have been insistent that we acknowledge that they plan to continue developing React Router indefinitely. I’m still going to call this a divorce. A divorce that signifies a growing dissatisfaction with the direction and inherent inflexibility of React itself.
Why Remix is Breaking Up with React
Remix's philosophy has always been about embracing web standards, leveraging the power of the server, and championing progressive enhancement. A core tenet of their approach is transferable knowledge, meaning that what you learn building with Remix should largely apply to general web development, not just a specific framework's ecosystem.
The framework itself centers on route-level data loading and mutations via standard HTML forms as core concepts, directly leveraging HTTP verbs and browser primitives. This contrasts sharply with React's increasingly component-centric view of the world, especially around data management, which often requires learning framework-specific abstractions. If I had to pinpoint the catalyst for this decision to leave React behind, it was likely the introduction of RSC (React Server Components).
Remix is now free to experiment with other ideas and paradigms. This could include exploring a more granular, islands-like hydration strategy, shipping JavaScript only where true interactivity is needed, and prioritizing faster initial page loads and resilience. Beyond traditional web paradigms, it also appears they are keenly interested in building a framework that is LLM-first. This is going to be a challenge, as models often default to generating React code, even when not explicitly prompted. I’ll be rooting for them on this one.
React's Missteps
Much of this decade, and even going back to the last, has seen React introduce features that have been met with mixed reactions and added significant complexity. Hooks were intended to solve the problem of “mixins are too hard,” but came with their own problems. Particularly, they introduced counter-intuitive "Rules of Hooks" and often led to convoluted state management with useEffect
dependency arrays that could easily create spaghetti code.
Then came Suspense and Concurrent Mode, features largely necessary to address problems created by React's own component-centric data loading model. Suspense, in particular, was introduced to preserve the illusion of component isolation, allowing a component to "suspend" rendering until its data is available, even if that data is part of a larger, interdependent page. Yet, their practical implementation has been slow to stabilize, and their complexity often feels disproportionate to the problems they solve – especially for frameworks that already manage data loading at a higher, more holistic level like Remix's loader
functions. For many, these features simply added new layers of mental overhead without a clear, universally beneficial payoff.
Perhaps the most contentious has been React Server Components (RSC). While designed to address the problem of excessive client-side JavaScript and enable closer integration with server logic, RSC's adoption outside of Next.js has been lukewarm. The mental model of "server components" vs. "client components" is still a source of confusion, and the build-time and runtime implications are non-trivial. It feels, to many, like a complex solution to a problem that other frameworks are solving with simpler, more platform-aligned approaches. In other frameworks, a clear distinction between server code (e.g., in .astro
files) and client code (written in a framework and then opted-in with client:
directives) has been important to maintain sanity during development. It’s not without its drawbacks, but years later, this approach still feels like the right decision. And it brings some amount of glee any time confusion about RSC boundaries is tweeted, recalling Dan Abromov's claims to the contrary.
The Moat: How Early Success Shielded React
To understand why React could introduce such complexity without facing a significant backlash, we need to look back at its foundational strengths. React truly nailed two ideas in its early days:
-
JSX and UI Composition: The ability to define UI directly within JavaScript, leveraging the language's power for logic and component reuse, was groundbreaking.
-
View as a Function of State: The declarative paradigm, where developers describe the desired UI state and React handles the underlying DOM mutations, was a revelation. It vastly simplified UI development and eliminated a huge class of bugs.
These two innovations made React the most popular UI library and built its unparalleled ecosystem. This vast ecosystem – with its abundance of libraries, tools, community support, learning resources, and job opportunities – became React's moat. Choosing React isn't about its latest features; it's about the safety, familiarity, and talent pool provided by this entrenched ecosystem. This often means that any new ideas from the React core team, regardless of their intrinsic merit or added complexity, gain adoption because people are already committed to the ecosystem. It's the classic "no one ever got fired for buying IBM" phenomenon.
React's Opinionated Architecture
This dominance, however, comes with a significant caveat: React's inherent design often makes it incredibly difficult for frameworks to build upon it while maintaining a distinct philosophy. React has strong opinions that are deeply baked into its core, making innovation at the framework layer a challenging, if not Sisyphean, task.
Consider these examples of how React's opinions become architectural constraints for framework authors:
-
Opinionated State Management: React's reliance on Hooks (useState
, useEffect
, useRef
, etc.) dictates a specific paradigm for managing component-local state and side effects. While powerful for isolated UI concerns, this reactive, often implicit, flow can quickly lead to complex, interconnected useEffect
chains or "spaghetti code" for larger application state. Side effects, in particular, are not treated as a first-class concern, often requiring developers to carefully manage dependencies to avoid unexpected behavior. The "Rules of Hooks" (e.g., no conditionals) further constrain how you can organize logic, forcing patterns that might not feel natural for complex state transitions. If a framework believes, as I do, that state (not UI) is the most critical consideration for an application, and prefers a fundamentally different model — for instance, the actor model with explicit message passing for communication — trying to implement this within React is an uphill battle. You're constantly fighting against React's reactive primitives and its inherent bias towards handling state changes through the UI component tree, rather than as a first-class, independent concern.
-
Component-Centric Data Loading: React promotes the idea that a component should "know" how to fetch its own data. Features like Suspense for Data Fetching are designed to preserve the illusion of a component being standalone, suspending rendering until its data is available. However, a web page is rarely truly standalone; things that happen in one part of the page often influence others, and data is frequently aggregated from multiple sources for a single view. This component-level fetching can lead to performance pitfalls like network request "waterfalls" or duplicate fetching if not carefully managed. To mitigate this, meta-frameworks often have to implement complex solutions—such as overriding the global fetch()
function, as seen in Next.js (previously, I know they have gone back on that decision) —to optimize data loading across a page, even though the component-centric model of React subtly encourages inefficient patterns. If a framework, like Remix, is geared towards a more route-level data loading strategy where all data for a page is fetched before rendering, then React's solutions for component-level data loading provide little value, yet the complexity associated with them is still inherited.
-
The Implicit Nature of React's Runtime: React's reconciliation process and virtual DOM are tightly coupled with its state management and component lifecycle. This integrated approach, while powerful, offers few escape hatches or low-level hooks for framework authors to inject their own logic or optimizations without potentially breaking React's core assumptions. If a framework wants to generate highly optimized, static HTML by default and only "hydrate" specific interactive islands, React's full-page hydration model creates friction and requires workarounds.
The complexity of these fundamental architectural clashes ultimately forces framework authors to compromise their vision or, as Remix is doing, break away entirely.
Shifting Relationship with Frameworks
Over the years, React has often charted its own course, sometimes without close collaboration with the meta-frameworks that rely on it. This has occasionally led to a dynamic where feedback from framework maintainers wasn't fully incorporated, or where React itself introduced features that directly overlapped with or even superseded functionalities that frameworks had painstakingly built. React's expansion into areas like data fetching and server-side concerns, traditionally managed by frameworks, illustrates this trend. The intent might be to provide a more cohesive "full-stack" story, but it has meant frameworks have had to adapt to React's evolving roadmap rather than collaborating on a shared vision for the web's future.
A prime example of this dynamic is the relationship between React and Remix's approach to data loading. Remix pioneered a powerful route-level data loading mechanism through its loader
API, which fetches all necessary data on the server before the page renders, ensuring efficient waterfalls and a strong foundation for progressive enhancement. React Server Components later emerged as React's own answer to server-side data fetching and reducing client-side JavaScript. While the Remix team has publicly expressed appreciation for the goals of RSC, they have also, at times, voiced criticisms regarding its implementation and the complexities it introduces, particularly when compared to the more streamlined approach offered by their own loader
functions. I don’t know what the future looks like here for Remix, but the stated goal of making sure the framework works without bundlers points to the likelihood that they’ll not be pursuing RSC-like features themselves in the future.
Is This the Beginning of the End for React?
The question of whether this marks a turning point for React's dominance invites a comparison to past shifts in the ecosystem. Could Remix's move be the first crack in React's seemingly impenetrable dominance, mirroring the fate of Webpack?
Consider the parallels:
-
Webpack's Reign: Webpack held an insurmountable lead in bundling, with an ecosystem for everything.
-
Bundlers like Rollup and esbuild emerged, offering superior performance and simplicity, winning over the in the know crowd, but not significantly denting market share.
-
Then came Vite, a new pattern embracing native ESM and deferring bundling to production, fundamentally changing the developer experience. Every major framework quickly adopted Vite under the hood.
-
Webpack underwent a massive rewrite (Turbopack) but largely remained tied to its flagship framework (Next.js), losing its broader ecosystem adoption.
In the UI world, we see a similar dynamic in the rise of libraries like Preact and Solid. Preact offers a tiny, fast alternative that's compatible with the parts of React that people actually like, while Solid provides a highly performant, non-Virtual DOM approach to reactivity that maintains a familiar JSX syntax. These libraries are winning over developers who prioritize raw performance and a simpler mental model for reactivity.
However, if a true "Vite effect" is going to materialize in the UI space, we're going to need to see more consolidation around one of these alternative UI libraries, backed by multiple meta-frameworks. A key difference from the bundler landscape is that Rollup, for instance, didn't have a higher-level dev/prod bundler tool, which allowed Vite to fill that role for various frameworks. In the UI space, many of these promising alternatives come with their own tightly coupled meta-frameworks (e.g., Solid with SolidStart), potentially hindering broader adoption across the ecosystem.
I must acknowledge that a UI framework is far more sticky than a bundler. But the parallels are striking. React's ever-increasing complexity, its strong opinions that clash with alternative architectural philosophies, and its tendency to absorb responsibilities previously focused on by meta-frameworks could leave it vulnerable.
The future of web development is always in flux, and Remix's decision is a powerful signal that the status quo may not hold for much longer.