Philosophy
Remix V3 is built on six core principles. These are not abstract ideals --- they are concrete rules that shape every API, every package, and every line of documentation. Understanding them will help you understand why Remix works the way it does.
1. Model-First Development
What it means: Every piece of Remix --- code, documentation, tooling --- is designed to be understood by both humans and AI language models.
Why it matters: AI coding assistants are becoming a standard part of how developers work. If a framework's APIs are inconsistent, poorly documented, or rely on implicit behavior, AI tools struggle to help you use it effectively. Remix is designed so that when you ask an AI assistant to "add authentication to my Remix app," it can give you a correct, working answer.
In practice:
- APIs follow consistent, predictable naming patterns.
- Documentation is written in clear, structured prose that works well as context for language models.
- Code examples are complete and runnable, not abbreviated snippets that require guesswork.
2. Build on Web APIs
What it means: Remix uses standard web APIs everywhere possible instead of inventing its own abstractions or relying on platform-specific APIs.
Why it matters: When you learn Remix, you are also learning the web platform. The skills you build are portable --- they transfer to browser code, to other frameworks, and to future platforms that support the same standards. Your code is not locked into any single runtime or vendor.
The standards Remix uses:
| Instead of... | Remix uses... |
|---|---|
Node.js http.IncomingMessage | Request (Fetch API) |
Node.js http.ServerResponse | Response (Fetch API) |
Node.js stream.Readable | ReadableStream (Web Streams) |
Node.js Buffer | Uint8Array |
Node.js crypto | Web Crypto API |
| Custom file abstractions | Blob and File |
Why does this matter in practice?
Code you write with Request, Response, and ReadableStream works in the browser, on a Node.js server, inside a Cloudflare Worker, and in Deno --- all without changes. Code written against node:stream or Buffer only works in Node.js (and runtimes that emulate it). Choosing web standards means your code is future-proof and portable by default.
Example: In many Node.js frameworks, you read a request body like this:
// Node.js-specific --- only works in Node.js
let chunks = []
req.on('data', (chunk) => chunks.push(chunk))
req.on('end', () => {
let body = Buffer.concat(chunks).toString()
})In Remix, you use the standard Fetch API:
// Web standard --- works everywhere
let body = await request.text()3. Religiously Runtime
What it means: Remix does not require a bundler, compiler, or build step to work. You can run your code directly, and your tests run without any compilation phase.
Why it matters: Build tools add complexity. When something goes wrong, you have to figure out whether the bug is in your code or in the build pipeline. By keeping everything runnable at the runtime level (meaning you can just execute your files directly), Remix stays simple to debug, fast to test, and easy to understand.
In practice:
- No Remix API depends on a bundler transformation to function correctly.
- Tests execute your actual source code, not a compiled output.
- You can use a bundler if you want to (for example, to optimize production builds), but you never have to.
What is a "bundler"?
A bundler is a tool (like Webpack, Vite, or esbuild) that takes your many source files and combines them into fewer, optimized files for production. Some frameworks require a bundler to work at all --- Remix does not.
4. Avoid Dependencies
What it means: Remix aims to have as few external dependencies as possible. When an external library is used, it is wrapped in an internal abstraction so it can be replaced later.
Why it matters: Every dependency is a risk. Libraries get abandoned, introduce breaking changes, or have security vulnerabilities. By minimizing what Remix depends on, the framework stays stable, secure, and under its own control. The long-term goal is zero external dependencies.
In practice:
- If Remix needs functionality that an external library provides, the Remix team wraps it behind a stable internal interface.
- If a better alternative appears (or the team writes their own implementation), the dependency can be swapped out without changing any public API.
- You, the developer, never interact with these internal dependencies directly, so replacements are invisible to your application code.
5. Demand Composition
What it means: Remix is built from single-purpose packages that each do one thing well. Every package works independently and can be used on its own, replaced, or left out entirely.
Why it matters: You should never be forced to adopt an entire framework just to use one feature. If you only need Remix's router, you can use just the router. If you prefer a different authentication system, you can swap out Remix's auth package without touching anything else.
Example: Here is Remix's cookie package being used completely on its own, with no router, no server, no other Remix package:
import { createCookie } from 'remix/cookie'
let preferences = createCookie('prefs', {
maxAge: 60 * 60 * 24 * 365, // 1 year
httpOnly: true,
secure: true,
})
// Parse cookies from a request header
let values = await preferences.parse(request.headers.get('Cookie'))
// Serialize a value into a Set-Cookie header
let header = await preferences.serialize({ theme: 'dark' })This package has no opinion about what server you use, what router you use, or whether you use any other part of Remix.
6. Distribute Cohesively
What it means: Despite being built from many independent packages internally, Remix is distributed as a single remix package on npm.
Why it matters: Composition is great for architecture, but managing 40 separate package versions is terrible for developer experience. With a single package, you run one install command, get one version number, and never have to worry about compatibility between pieces.
In practice:
# One install, everything included
npm install remix// Import from subpaths --- clean and discoverable
import { createRouter } from 'remix/fetch-router'
import { createCookie } from 'remix/cookie'
import { createTable } from 'remix/data-table'
import { authenticate } from 'remix/auth'Each subpath (remix/fetch-router, remix/cookie, etc.) maps to an independent internal package. You get the ergonomics of a single framework with the architecture of a composable toolkit.
Summary
| Principle | In short |
|---|---|
| Model-First Development | Designed for humans and AI assistants alike |
| Build on Web APIs | Standards-based, portable, future-proof |
| Religiously Runtime | No build step required --- ever |
| Avoid Dependencies | Fewer moving parts, more stability |
| Demand Composition | Use what you need, replace what you don't |
| Distribute Cohesively | One package, one version, zero hassle |
These principles work together. Web standards make composition possible (standard interfaces are easy to mix and match). Composition makes runtime execution practical (small, focused packages don't need complex build pipelines). And distributing cohesively makes the whole thing pleasant to use.
Next Steps
- Why Remix? --- See how these principles translate into concrete benefits.
- Overview --- Go back to the high-level introduction.
- Getting Started --- Start building with Remix.