On April 30, Remix shipped its 3.0 beta — and for the first time in the framework's history, the install does not pull in react. The component model is a fork of Preact. The rendering is imperative. The migration path from Remix 2 is, officially, that there isn't one. If you came here expecting a version bump, this isn't that.
The team telegraphed all of this last August in “Wake up, Remix!” — a post that read, at the time, like a manifesto whose footnotes hadn't been written yet. Eight months later the footnotes have arrived, and the framework underneath the name is a different framework.
What actually shipped
The beta preview describes Remix 3 as a “full-stack” framework rather than the “center stack” one it used to be. In practice that means it now ships its own answers for routing, request handlers, middleware, sessions, auth, forms, uploads, asset compilation, and a database client. Remix 2's job was to bridge React Router to the server; Remix 3's job is to be the server, the client, and the bridge.
The component layer is the most visible break. Imports come from remix/ui — remix/ui/button, remix/ui/glyph, and so on. Event wiring runs through an on() function rather than JSX event props. Cross-component communication uses native CustomEvent instead of prop-drilling or context. Reusable behavior is composed with mixins. None of this is a small detail of taste — it's the public API.
There's also a new primitive called Frames: server-rendered fragments with a src attribute that load and navigate independently of the page around them. The bookstore demo in the beta uses a Frame to update a cart without touching the rest of the document. If that sounds familiar, it should — Frames are Remix's take on the idea Turbo and HTMX have been quietly proving for years.
Imperative is back
The clearest way to feel the shift is to look at state. In Remix 2 — which is React under the hood — a counter is the textbook hook:
import { useState } from "react";
export function Counter() {
const [n, setN] = useState(0);
return <button onClick={() => setN(n + 1)}>{n}</button>;
}In Remix 3, the same component is a class with a plain closure variable and an explicit update call. The shape, based on the patterns in the beta preview, looks roughly like this:
import { Component } from "remix/ui";
import { on } from "remix/ui/events";
export class Counter extends Component {
n = 0;
render() {
return on("click", () => { this.n++; this.update(); },
<button>{this.n}</button>
);
}
}No hook rules. No dependency arrays. No memoization ritual. You change a variable; you tell the framework you changed it. LogRocket's writeup frames this as developers “tell[ing] the framework when something has changed” rather than declaring desired state — which is, depending on your priors, either a return to sanity or a return to 2013.
Remix components build on web primitives like EventTarget and avoid the runtime semantics of React hooks, giving you back normal JavaScript control flow and execution.
That's the pitch in one sentence. You don't have to agree with it to notice that it's a coherent position, not a vibes-based one.
Why Preact, and why fork it
The choice of Preact is less surprising than it sounds. Shopify, which acquired Remix in 2022, already runs Preact in production at scale, and Preact's surface area is small enough to fork without inheriting a roadmap. The “Wake up” post lists six principles for Remix 3; two of them — “Religiously Runtime” and “Avoid Dependencies” — explicitly preclude treating React as load-bearing. You cannot promise “no critical dependencies” and then depend on the React team's release cadence.
The fork also lets Remix change the component contract. Preact's hooks are gone; what's left is the diffing core. On top of that the team has built a class-based, mixin-composed model that looks much more like a Web Components story than a React one. The result is a virtual DOM library that exists to serve Remix's framework, not the other way around.
The split: Remix 3 vs. React Router 7
If you have a Remix 2 app in production, the headline is not “upgrade to 3.” It's “there is no upgrade to 3.” The official continuity path is React Router 7, which absorbed most of Remix 2's data-loading and form-action APIs last year and is, in effect, the React-flavored continuation of the project. Remix 3 is a new framework that kept the name.
Concretely, the fork in the road looks like this:
- Staying on React, big existing app:
react-router@7. Same loaders, same actions, same mental model. - New project, willing to bet on a small-but-opinionated stack:
remix@3. Expect to relearn the component model. - Shopify Hydrogen users: hold. Hydrogen still rides on Remix 2 / React Router, and Shopify hasn't published a Remix 3 timeline for it.
The loudest community thread on the GitHub repo asked the team to reconsider releasing this as Remix 3 at all — to ship it under a new name and let React Router 7 inherit the brand. The team didn't, and you can read that decision as either confidence or stubbornness. Either way, it means the word “Remix” now refers to two incompatible things depending on which year the speaker is from.
The Model-First problem
Of the six principles in the manifesto, the one that drew the most heat is “Model-First Development”: the idea that the framework's source, docs, and abstractions should be optimized for LLMs to read and write. The InfoQ coverage captured the split — Tanner Linsley of TanStack called the bet ambitious and good for the ecosystem; other developers read it as Shopify priorities leaking into open source.
It's worth taking the principle on its own terms. “Optimize for LLMs” in practice tends to mean: fewer magic globals, fewer implicit hook orderings, more explicit imports, more predictable file layouts. Those are also things a senior engineer asks for. The disagreement is less about the destination and more about who's being named as the user. A framework that says “we designed this for AI” in 2026 will get a different reception than the same framework saying “we designed this to be legible” would have in 2022, even if the code is identical.
What to watch
Three things will decide whether Remix 3 lands or quietly becomes a research project Shopify funds:
- Hydrogen. Shopify's own storefront framework is the natural first big consumer. If it ports, the bet was real; if it doesn't, the bet was a blog post.
- The escape hatch. “No migration path” works for new apps and fails for existing Remix 2 codebases. Some kind of interop story — even a compat shim — will likely emerge, and the shape of it will say a lot about which audience the team is actually courting.
- The component model under load. Imperative
this.update()is fine in a Counter. It gets interesting at the size of a real dashboard, and that's where reactivity systems (Signals, Svelte 5's runes, Vue's refs) have spent the last three years iterating. Remix is choosing not to use any of them. That's a strong opinion, and strong opinions get tested.
For now, the practical advice is boring: if you're shipping production code this quarter, React Router 7 is the path. If you're starting a side project and you've been waiting for a framework that takes the runtime seriously and treats React as optional rather than foundational, the beta is installable today. The interesting question isn't whether Remix 3 is better than Remix 2. It's whether any framework should still be the “React framework” by default — and Remix is the first big project to answer no out loud.
