remix-run/remix
github.com/remix-run/remix ↗ · 2509 files · 12.7 MB
Welcome to Remix 3! This is the source repository for Remix 3.
Overview
Remix 3 is a full-stack TypeScript framework assembled from focused packages and distributed through the generated `remix` umbrella package. It connects typed Fetch routing, middleware, streaming server rendering, browser hydration, asset compilation, authentication, persistence, and testing through Web APIs. This brief investigates commit 27bd7a4401594ec1acbe30ee3937518832270a30 through public tracked source; its inventory is bounded, and tests and CI were inspected, not executed.
System map
The repository separates portable framework packages from Node tooling, browser runtime code, example applications, and documentation generators. The `remix` distribution is generated from a canonical alias manifest rather than maintained as an independent implementation. The inspected application template starts a Node HTTP server, but routing and rendering boundaries use Web requests, responses, and streams. Storage is selected through interfaces and adapters rather than one repository-wide database.
- The root is a private ESM workspace pinned to pnpm@10.34.2 with Node >=24.3.0; its recursive scripts build, test, and typecheck workspace packages.
- The pnpm workspace includes packages, demos, documentation apps, benchmarks, CLI fixtures, the application template, and maintenance scripts.
- scripts/generate-remix.ts generates source re-exports, package exports, dependencies, copied READMEs, guides, and the documentation index from the umbrella manifest.
- The published-facing `remix` manifest exposes focused subpaths for components, auth, schema validation, databases, file storage, and middleware; its root export is types-only in the inspected source exports.
- The generated CLI executable calls runRemix(process.argv.slice(2)) and exits with its numeric result, while the reusable CLI API lives in @remix-run/cli.
- The application template runs TypeScript/JSX directly through remix/node-tsx in development and production; development uses Node watch mode, and HMR has a separate command.
- template/server.ts adapts router.fetch to Node HTTP, defaults PORT to 44100, recognizes REMIX_NODE_HMR and HMR_PROXY_PORT, and closes connections on SIGINT or SIGTERM.
- The server component renderer produces ReadableStream<Uint8Array>; the browser runtime owns document frames, scheduling, styles, module caches, and navigation listeners.
- SessionStorage defines asynchronous read(cookie) and save(session) operations; cookie storage embeds session data, whereas memory storage uses a process-local Map.
- The bookstore demo selects SQLite with foreign keys, uses an in-memory database for NODE_ENV=test, and otherwise stores its database beside SQL migrations and seed data.
- Maintenance scripts are a separate ESM workspace with Octokit, semver, YAML, and shared documentation dependencies, plus remix test and tsc validation commands.
- Repository automation includes generated comment routing, iteration, proposal review, and pull-request review workflows in addition to normal build/test workflows.
Feature catalog
The primary users are application developers consuming framework APIs and CLI commands. Features include routing, server and browser UI, assets, authentication, database management, storage, and testing. The bookstore and social-auth applications expose concrete examples of these capabilities. This catalog covers inspected public surfaces and documented package families; it is not an exhaustive enumeration of every export or demo screen.
- runRemix dispatches new, completion, assets, doctor, db, routes, test, version, and help commands, including --help and --version aliases.
- `remix new <target-dir>` scaffolds an application and accepts --app-name and --force.
- `remix assets` lists browser-reachable assets; `remix assets inspect <url-or-file>` reports status, URL, file path, and matching denial information.
- Database CLI help exposes wipe, migrate, rollback, status, seed, and reset, with migration targeting, rollback dry-run, connection environment selection, and force flags for destructive operations.
- Routing supports method-specific registration, controller mapping, mounted route installers, automatic HEAD fallback from GET, and 405 responses with Allow headers.
- The template exposes a home page and /assets/*path; its home controller returns component-rendered HTML and its asset controller falls back to a 404 response.
- render middleware provides streaming component HTML, nested frame resolution, and asset-backed client-entry metadata; the browser runtime exposes ready(), flush(), dispose(), and frame handles.
- Template assets support development source maps, watching, optional component HMR, production minification, file allowlists, package allowlists, and test-file denial.
- The auth public entrypoint exports credentials authentication, OAuth/OIDC lifecycle helpers, and provider factories for Auth0, Facebook, GitHub, Google, Microsoft, OIDC, Okta, and X.
- The bookstore route map includes catalog browsing by genre and slug, search, contact forms, login and registration, password recovery, account settings, order views, cart APIs, checkout, and administrative resources.
- The social-auth GitHub controller redirects into external login, resolves callback identities into persistent users/accounts, stores authentication state, and reports configuration or login failures through session flash messages.
- Documented package families also provide schema validation, MySQL/Postgres/SQLite queries, S3 file storage, multipart uploads, HTTP headers, compression, CORS/CSRF protection, and headless UI primitives.
Data flow
HTTP input becomes a Web Request and request context before middleware and route dispatch. Component output becomes a byte stream carrying HTML and client-entry metadata, which the browser uses to activate components and frames. OAuth data crosses a session-backed redirect boundary before application-owned identity persistence. CLI inputs and configuration follow separate Node filesystem and process boundaries.
- For one-argument handlers, createRequestListener constructs a Web Request, invokes the handler, and sends synchronous or asynchronous responses through sendResponseForRequest; request-construction and handler failures use separate error paths.
- Router.fetch creates RequestContext, assigns its router, and dispatches through global middleware before matching routes against context.url.
- dispatchMatch merges matched parameters into context.params and executes route middleware or the handler while racing against request abort.
- renderWith stores a per-request renderer as both a context key and context.render; render connects request URL, abort signal, frame resolution, and client entries to renderToStream and createHtmlResponse.
- A file:-based clientEntry ID is split into source ID and export name, resolved with assets.getScriptEntry, and returned with href, import map, and preload metadata.
- The template browser entry imports each requested module through importModule, selects its named export, rejects non-functions, and supplies that loader to run().
- Asset requests accept GET/HEAD, normalize transform query data, parse mapped paths, and route CSS through styleCompiler; compiled script responses enforce fingerprints and distinguish missing/disallowed files from invalid transforms.
- Session middleware parses the signed cookie, optionally validates embedded expiration, loads SessionStorage, installs context.session, then saves after downstream handling and appends Set-Cookie when storage returns a value.
- External login generates state and a PKCE verifier, stores the transaction under __auth by default, and returns the provider authorization redirect; callback processing validates provider/state before invoking the provider runtime.
- resolveExternalAuth looks up the provider account, extracts profile details, updates or creates a user, optionally links by profile-derived email, then creates or updates the auth-account row.
- Scaffolding resolves the target directory and application name, copies token-substituted template files, and rewrites package name, Remix dependency version, development dependencies, and Node engine constraints.
- Route inspection launches a Node subprocess that dynamically imports app/routes.ts, normalizes the exported routes object, writes JSON to stdout, and returns that output to the parent loader.
Behavior traces
The investigation followed three concrete flows across implementation boundaries: scaffolding, template startup/rendering/hydration, and external login with persistence. Additional traces explain frame reloads and asset inspection. These are ordered source walkthroughs, not observed executions. Failure branches are included where they materially change the result.
- Scaffold entry: cli-entry.ts → runRemix → extractGlobalOptions → resolveCliContext → runCommand('new') → dynamically imported runNewCommand → parseNewCommandArgs → bootstrapProject.
- Scaffold writes: bootstrapProject → ensureTargetDirectory → resolveTemplateDirectory → copyTemplateDirectory → replaceTemplateValues → writeScaffoldPackageJson; a nonempty target fails unless force is enabled.
- Template startup: the start/dev command loads remix/node-tsx and server.ts; router module initialization creates static/render middleware and maps the controller; server.ts creates the HTTP listener and calls listen(port).
- Home request: createRequestListener → createRequest → router.fetch → createRequestContext → dispatchRouter → dispatchMatches → dispatchMatch → controller.home → context.render.
- HTML output: renderWith installs context.render; the render function calls renderToStream with frame/client-entry resolvers and the request signal, then wraps the stream using createHtmlResponse.
- Browser activation: template entry calls run → createStyleManager → createScheduler → createFrame(document) → startNavigationListener → topFrame.ready; hydrated exports are obtained through the entry's importModule loader.
- OAuth login: GitHub controller.login → startExternalAuth → getSession → createOAuthTransaction → sanitizeReturnTo → provider runtime createAuthorizationURL → session.set('__auth') → redirect response.
- OAuth callback persistence: controller.callback → finishExternalAuth → provider runtime handleCallback → resolveExternalAuth(db,result) → completeAuth → session.regenerateId(true) → session.set('auth') → redirect; enclosing session middleware subsequently saves the session.
- OAuth failure: finishExternalAuth rejects callback errors, missing/wrong-provider transactions, or invalid state and clears transaction data on failure; the demo controller catches errors, flashes a message, and redirects home.
- Browser frame reload: defaultResolveFrame creates HTML/frame headers, encodes submitted form data, calls fetch with mode='same-origin' and an abort signal, and rejects 5xx or non-HTML error/redirect responses.
- Server nested frame: resolveFrame resolves src against the current frame URL, prepares headers, follows redirects through context.router.fetch, requires HTML, and pipes the returned stream with the outer request's abort signal.
- Asset inspection: runAssetsCommand parses arguments → context.loadConfig → createAssetServer(watch:false) → getAssets or getAssetDetails → formatted stdout; finally it closes the asset server.
Testing signals
Tests and CI were inspected, not executed; no passing result or measured coverage is asserted. The sampled packages use the repository's own test framework with Playwright integration rather than establishing Vitest as the main runner. Source tests cover routing aborts, cookie defaults, asset policies/cache behavior, frame requests, and a fake-provider OAuth flow. Browser projects and integration job filters limit which environments a configured run exercises, and this inspection does not establish exhaustive coverage.
- The template test imports describe/it from remix/test and assertions from remix/assert; scripts also use `remix test`.
- The test runner discovers .test, .test.browser, and .test.e2e TypeScript/TSX files, defaults to a forks pool, and supports server, browser, and E2E test types.
- The test package uses `remix test`, offers a Bun server-test command, and declares Playwright as an optional peer dependency.
- The inspected template controller test checks GET / status, HTML content type, and an html element; that individual test does not assert browser hydration or interaction.
- Router abort tests assert rejection for pre-aborted requests, abort during handler processing, and aborted Request inputs.
- Session middleware tests verify protocol-derived Secure, explicit Secure overrides, and the default HTTP-only flag.
- Asset-server tests create temporary source fixtures and assert that only allowlisted script/style/configured file assets appear, excluding private and denied test files.
- Asset tests assert GET/HEAD support, POST fallthrough, no-cache headers, ETags, and a 304 response to matching If-None-Match.
- Frame tests mock fetch and verify posted FormData, frame headers, same-origin mode, abort signals, and resulting DOM content; a separate test checks same-origin mode for an explicitly external src.
- The OAuth integration test follows authorization, callback-cookie propagation, and protected dashboard access against a local fake provider; it does not demonstrate live provider compatibility.
- Component and UI Playwright configs select Chromium only; assets, component-HMR, the test framework, and the import-map polyfill select Chromium and Firefox.
- The test framework and multiple-import-map polyfill also explicitly configure Chromium and Firefox projects.
Risk map
The inspected security-sensitive boundaries include proxy-derived URLs, browser-accessible source, raw HTML, OAuth/session state, filesystem writes, and subprocess module loading. Several protections are explicit, including signed-session requirements, callback state checks, return-path normalization, and header reduction for cross-origin frames. Backend limitations and application configuration remain important to the security model. These are source observations and labeled hypotheses, not confirmed vulnerabilities or a complete security audit.
- trustProxy allows Forwarded and X-Forwarded headers to affect request host, protocol, and client address; its documented precondition is a trusted proxy that overwrites those headers.
- Browser asset exposure is an explicit policy boundary: the template allows routes.ts and public descendants, allows the remix package, denies test files, and configures mounts outside its root for monorepo use.
- Injected asset-runtime files are allowed before configured allow/deny matching, while ordinary allowed files can subsequently be denied; policy changes must preserve this intentional exception.
- HostProps.innerHTML accepts UnsafeHTML and explicitly performs no sanitization; untrusted content passed through this API is an application-owned trust decision.
- The default browser frame resolver restricts fetch to same-origin mode but does not sanitize HTML; custom resolvers own request, redirect, and content trust policies.
- Server frame resolution removes body/transport headers, strips sec-fetch headers, limits redirects to 20, and uses a reduced header allowlist when the frame URL changes origin.
- OAuth callback processing validates stored provider and state and clears the transaction after success or failure; returnTo normalization rejects nonlocal origins and double-slash paths.
- OAuthTransaction.providerState documentation requires providers to encrypt sensitive values because session storage does not guarantee confidentiality.
- Cookie session storage serializes all session data into JSON and explicitly warns that regenerated old session IDs cannot be deleted; session rotation therefore has different revocation properties across storage backends.
- Session middleware requires signed cookies and defaults HTTP-only/Secure, but honors explicit insecure overrides; Secure otherwise depends on the interpreted request protocol.
- Scaffolding writes to a user-resolved target directory outside any fixed repository boundary; --force permits a nonempty directory and template copying overwrites matching files.
- Route inspection executes application module code in a child process through dynamic import; process separation is not a sandbox for an untrusted checkout. (inferred)
Extension points
Composition is the main extension mechanism: factories, middleware, route installers, renderers, provider runtimes, and storage interfaces connect focused modules. The umbrella alias manifest controls public distribution wiring. CLI features use explicit command dispatch and structured JSON configuration. Browser extension hooks carry their own trust responsibilities.
- Add umbrella aliases through packages/remix/manifest.json and its generator; the manifest explicitly excludes @remix-run/ui from automatic umbrella inclusion.
- RouterOptions exposes matcher, defaultHandler, and middleware replacement points, with createMultiMatcher as the default matcher.
- RouteInstaller receives a RouteBuilder; mount joins route prefixes and invokes the installer on a child builder.
- Controllers and actions can carry middleware; mapController merges controller middleware with each action's middleware before registration.
- Applications augment remix.RouterTypes.context using middleware-derived context; the template and bookstore demonstrate that typed integration.
- renderWith accepts a per-request renderer factory, allowing custom input/output rendering while keeping the same context.render/context-key integration.
- run accepts a required module loader, an optional frame resolver, and optional client-entry preload processing; default frame behavior can be replaced.
- createOAuthProvider binds a stable name to createAuthorizationURL, handleCallback, and optional refreshTokens hooks, with normalized profile/account/token result types.
- SessionStorage is the backend contract; read returns a Session and save returns a cookie value, null for no update, or an empty string for destruction.
- createBookstoreRouter accepts injected Cookie and SessionStorage values, and createGitHubAuthController accepts an injected provider registry.
- The CLI extends through runCommand's explicit lazy-import branches and RemixConfig's assets/db/doctor/test sections; config parsing rejects unknown top-level properties.
- The asset server receives loader and HMR configuration; the template installs componentHmr() and a lazily supplied browser HMR channel.
Contributor handoff
Read the owning package and its actual runtime boundaries before editing generated surfaces or application examples. Tests and CI were inspected, not executed, and this bounded source investigation supplies no passing baseline. The requested environment-file entrypoint was intentionally not read under the user's prohibition on environment files and secrets. Future implementation work should preserve package boundaries, generated wiring, source-run tests, and public API documentation.
- Read AGENTS.md first for dedicated public source entrypoints, implementation-only src/lib, direct cross-package imports, and Web API preferences; CLAUDE.md delegates to that guide.
- Use pnpm and the root Node engine requirement; the workspace catalog centralizes TypeScript, Playwright, formatting, and other shared versions.
- For request changes, read server.ts, router.ts, routes.ts, controller actions, and render integration together; these files connect the template's HTTP, route, and HTML boundaries.
- For auth changes, read transaction helpers, callback validation, completeAuth, session middleware, and the chosen backend; backend deletion semantics affect the meaning of session rotation.
- Avoid manually changing generated umbrella entrypoints: cli-entry.ts is marked generated, and generate-remix.ts owns umbrella exports and documentation copies.
- CLI source scaffolding requires the prepared package template; its error directs developers to the CLI prepack script, which syncs that template.
- Use separate type imports/exports, .ts extensions, let for locals, const at module scope, and native private fields; Oxfmt enforces 100-column, semicolon-free, single-quoted formatting.
- Tests run from source without a build; the documented narrow commands include package-filtered tests/typechecks and --only test-name selection. These are recommended future checks, not commands executed during this investigation.
- The recommended fast loop includes metadata validation, lint, changed tests, and changed typechecks; changed commands compare against origin/main and include uncommitted changes.
- Public API changes require related docs, JSDoc, examples, and tests; changes affecting published packages require change files.
- Preserve SQL migrations as immutable artifacts; the migration decision rejects imports of evolving application schemas because they can alter old migrations and risk data loss.
- Inspect browser project configuration before claiming cross-browser coverage: the component/UI configs list only Chromium while several supporting packages list Chromium and Firefox.
Languages
| Language | Files | Share of source |
|---|---|---|
| TypeScript | 1706 | 95% |
| JSON | 279 | docs/config |
| Markdown | 276 | docs/config |
| JavaScript | 85 | 5% |
| YAML | 31 | docs/config |
| CSS | 14 | docs/config |
| HTML | 13 | docs/config |
| SQL | 9 | 0% |
Start reading here
Probable source entrypoint: packages/ — inferred from the directory tree and manifest, not the upstream docs.
Source roots
packages/ (1700 files)
Repository structure
| Top-level path | Files |
|---|---|
packages/ | 1700 |
demos/ | 418 |
docs/ | 242 |
scripts/ | 50 |
.agents/ | 33 |
.github/ | 26 |
template/ | 17 |
decisions/ | 6 |
Docs found: AGENTS.mdCLAUDE.mdCONTRIBUTING.mdREADME.mddocs/api/README.mddocs/guides/README.mddocs/guides/app/actions/docs/chapters/01-start-here.mddocs/guides/app/actions/docs/chapters/02-routing-and-controllers.mddocs/guides/app/actions/docs/chapters/03-request-handling.mddocs/guides/app/actions/docs/chapters/04-rendering-ui.md
Package
package.json — remix-the-web, 7 declared dependencies.
@oxlint/pluginsoxc-parseroxfmtoxlintoxlint-tsgolintremixtypescript
CI & config entrypoints
README.md— Repository readmeAGENTS.md— Agent instructionsCLAUDE.md— Claude instructions.env.example— Required env varspackages/ui/playwright.config.ts— Playwright e2e configpackages/multiple-import-maps-polyfill/playwright.config.ts— Playwright e2e configpackages/test/playwright.config.ts— Playwright e2e configpackages/component-hmr/playwright.config.ts— Playwright e2e configpackages/component/playwright.config.ts— Playwright e2e configpackages/assets/playwright.config.ts— Playwright e2e config
Structural health signals
Average score 7.1/10 across 96 analyzed source files; 28 hotspots surfaced.
demos/timeboxer/app/actions/schedules/public/schedule-grid.tsx— 1654 lines, score 4 (Large file, Deep nesting)packages/data-table/test/driver-integration-contract.ts— 557 lines, score 4.5 (Large file, Deep nesting)packages/data-table/src/lib/query.ts— 991 lines, score 5 (Large file, Deep nesting)
These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.
Recent history
27bd7a4401592026-10-01 — Use atomic writes in HMR tests (#11960)6f8acb3c6b582026-10-01 — Fix release readiness and report the published Remix tag (#11959)70cdc64df8fa2026-10-01 — Release v3.0.0 (#11954)114d151388d42026-10-01 — Update install instructions for the stable Remix release (#11957)697c7e98c2132026-10-01 — Preserve encoded API documentation links