Notes on Remix 3

← All notes

The benefits of Remix 3 aren’t about Remix at all — they’re about admitting that the React-first habit was making every other layer of the stack harder than it had to be

by A Developer ·  May 28, 2026  ·  8 min read

A framework that walks away from React in 2026 sounds like a stunt. Spend an afternoon writing routes, handlers and components in it and the move stops feeling brave and starts feeling overdue. Here’s the case for taking it seriously.

Picture the moment you opened a new app/ folder in the last framework you tried. Before you wrote a single line of feature code, you had decisions to make about hydration, about bundler config, about which copy of React the server and client would share, about which version of the data-fetching pattern you’d use this year. None of those decisions had anything to do with the thing you were trying to build.

The pitch for Remix 3 is that almost all of those decisions go away. Not because Remix is clever about resolving them — because the framework removed the layer they live in. There is no React, no virtual DOM, no bundler config, no separate compiler. The server takes a Request and returns a Response. The client runs the same JSX you served, hydrated by a small runtime that you could read in an afternoon.

Most of the “benefits” people list for Remix 3 are downstream of that single move. Once the framework stops depending on React, a lot of things you thought were unavoidable stop being unavoidable.

It’s built on Fetch, all the way down

Every handler in Remix 3 has the same shape: take a Request, return a Response. That isn’t a marketing line — it’s the actual interface, expressed in standard Web APIs that already work in Node, Deno, Bun, Workers, and any other runtime that implements Fetch.

What this gets you in practice is that the parts of your codebase that used to be framework-shaped become standards-shaped. A function that reads form data uses request.formData(). A function that sets a cookie returns a Response with a Set-Cookie header. A function that streams HTML returns a Response whose body is a ReadableStream. These all worked yesterday. They’ll work tomorrow. They will outlive any framework that builds on them.

The handler signature stops being “a Remix thing.” It’s a Fetch thing. You could lift any of these handlers into Cloudflare Workers tomorrow with no changes.

The component model fits in your head

Remix 3 components are not React components without hooks. They are a different question being asked. Each component is a function that returns a render function. State lives in the closure of the outer function. Re-renders happen when you explicitly ask for them via handle.update(). There’s no hook ordering rule, no dependency array, no double-render in development to teach you about purity.

The whole model can be written in five lines:

function Counter(handle) {
  let count = 0
  return () => (
    <button mix={on('click', () => { count++; handle.update() })}>
      {count}
    </button>
  )
}

That’s the entire mental model. If you’ve ever tried to teach a teammate when to use useEffect, when to use useLayoutEffect, when a ref is the right answer instead of state — you know how much surface area you’re saving here.

Everything ships in one package

The whole framework — router, components, data validation, session storage, file storage, middleware, cookies, fetch proxying, even the test runner — ships from one npm package called remix. You import what you need via subpaths:

import { createRouter } from 'remix/router'
import { compression } from 'remix/middleware/compression'
import { session } from 'remix/middleware/session'

The version-bump problem that has haunted full-stack JS for ten years disappears. There is one number to upgrade. The router and the session middleware can never be on incompatible versions because they’re the same package.

It also means the docs are coherent. Open the Remix package in node_modules/remix/src and you’ll find a README beside nearly every subpath. The skill files in your scaffold tell the AI agent exactly which subpath does what. There is no second site to chase.

Server-first is the default, not the discipline

Other frameworks let you do server rendering. Remix 3 makes you do it. The first version of your route should already return correct HTML, the correct status code, the correct redirect on POST — before you write a single line of client-side code.

The payoff is that hydration becomes a choice, not a requirement. A static page has no clientEntry() and ships no JS. A page with one interactive widget hydrates only that widget. You don’t opt out of interactivity; you opt in to it, one component at a time.

What you give up

It would be unfair to write all this without naming the cost.

You give up the React ecosystem. Not just React itself — every library that assumes a React runtime: react-query, framer-motion, half of Radix, every CSS-in-JS library that ships its own JSX runtime, every component vendor that hands you a <Button> with useState baked in. Remix 3 has its own primitives — animation, components, forms, tables — but they are new, and there are fewer of them than React has.

You also give up the ambient familiarity. Engineers who have only ever worked in React will trip on the component model the first day. There is no useState. handle.update() is a deliberate keystroke. That is part of the design, and it’s part of why the runtime is small, but the learning curve is real.

The thing that actually changed

The honest answer to “why use Remix 3” isn’t the bundle size, or the streaming, or the Fetch API. It’s that the framework no longer treats your app as something layered on top of React. It treats your app as something layered on top of the web.

That sounds like a slogan, but it shows up in small ways for the whole time you’re writing code. Forms work without JavaScript. Cookies are cookies. Response objects are Response objects. The browser does most of the work, and the framework gets out of the way for the parts the browser already knows how to do.

Whether that’s worth it for your codebase is your call. But it’s the first full-stack JS framework in a while where the answer to “what does this depend on?” is short enough to write on a postcard.