@remix-run/cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/README.md +0 -3
  2. package/dist/lib/bootstrap-project.d.ts.map +1 -1
  3. package/dist/lib/bootstrap-project.js +32 -9
  4. package/dist/lib/cli.d.ts +29 -0
  5. package/dist/lib/cli.d.ts.map +1 -1
  6. package/dist/lib/cli.js +33 -14
  7. package/dist/lib/commands/completion.d.ts.map +1 -1
  8. package/dist/lib/commands/completion.js +5 -1
  9. package/dist/lib/commands/doctor.js +18 -18
  10. package/dist/lib/commands/help.d.ts.map +1 -1
  11. package/dist/lib/commands/help.js +9 -33
  12. package/dist/lib/commands/routes.js +3 -3
  13. package/dist/lib/commands/test.d.ts +1 -1
  14. package/dist/lib/commands/test.d.ts.map +1 -1
  15. package/dist/lib/commands/test.js +8 -4
  16. package/dist/lib/completion.d.ts.map +1 -1
  17. package/dist/lib/completion.js +4 -106
  18. package/dist/lib/controller-files.d.ts +0 -1
  19. package/dist/lib/controller-files.d.ts.map +1 -1
  20. package/dist/lib/controller-files.js +3 -5
  21. package/dist/lib/controller-ownership.d.ts +9 -9
  22. package/dist/lib/controller-ownership.d.ts.map +1 -1
  23. package/dist/lib/controller-ownership.js +56 -91
  24. package/dist/lib/doctor/controller-findings.d.ts +1 -1
  25. package/dist/lib/doctor/controller-findings.d.ts.map +1 -1
  26. package/dist/lib/doctor/controller-findings.js +15 -87
  27. package/dist/lib/doctor/controller-fix-plans.d.ts.map +1 -1
  28. package/dist/lib/doctor/controller-fix-plans.js +13 -24
  29. package/dist/lib/doctor/controller-placeholders.d.ts +2 -3
  30. package/dist/lib/doctor/controller-placeholders.d.ts.map +1 -1
  31. package/dist/lib/doctor/controller-placeholders.js +18 -149
  32. package/dist/lib/doctor/controllers.js +1 -1
  33. package/dist/lib/doctor/project.js +60 -52
  34. package/dist/lib/doctor/types.d.ts +2 -2
  35. package/dist/lib/doctor/types.d.ts.map +1 -1
  36. package/dist/lib/errors.d.ts +0 -6
  37. package/dist/lib/errors.d.ts.map +1 -1
  38. package/dist/lib/errors.js +0 -11
  39. package/dist/lib/load-route-map-worker.js +17 -9
  40. package/dist/lib/route-map.d.ts +1 -1
  41. package/dist/lib/route-map.d.ts.map +1 -1
  42. package/dist/lib/route-map.js +29 -17
  43. package/package.json +4 -5
  44. package/src/lib/bootstrap-project.ts +39 -13
  45. package/src/lib/cli.ts +46 -15
  46. package/src/lib/commands/completion.ts +6 -1
  47. package/src/lib/commands/doctor.ts +18 -21
  48. package/src/lib/commands/help.ts +9 -43
  49. package/src/lib/commands/routes.ts +3 -3
  50. package/src/lib/commands/test.ts +10 -4
  51. package/src/lib/completion.ts +4 -151
  52. package/src/lib/controller-files.ts +4 -8
  53. package/src/lib/controller-ownership.ts +78 -141
  54. package/src/lib/doctor/controller-findings.ts +20 -97
  55. package/src/lib/doctor/controller-fix-plans.ts +13 -29
  56. package/src/lib/doctor/controller-placeholders.ts +17 -189
  57. package/src/lib/doctor/controllers.ts +1 -1
  58. package/src/lib/doctor/project.ts +60 -52
  59. package/src/lib/doctor/types.ts +1 -5
  60. package/src/lib/errors.ts +0 -12
  61. package/src/lib/load-route-map-worker.ts +19 -10
  62. package/src/lib/route-map.ts +61 -16
  63. package/template/.agents/skills/remix/SKILL.md +588 -0
  64. package/template/.agents/skills/remix/references/animate-elements.md +195 -0
  65. package/template/.agents/skills/remix/references/assets-and-browser-modules.md +130 -0
  66. package/template/.agents/skills/remix/references/auth-and-sessions.md +443 -0
  67. package/template/.agents/skills/remix/references/component-model.md +282 -0
  68. package/template/.agents/skills/remix/references/create-mixins.md +158 -0
  69. package/template/.agents/skills/remix/references/data-and-validation.md +379 -0
  70. package/template/.agents/skills/remix/references/hydration-frames-navigation.md +297 -0
  71. package/template/.agents/skills/remix/references/middleware-and-server.md +233 -0
  72. package/template/.agents/skills/remix/references/mixins-styling-events.md +213 -0
  73. package/template/.agents/skills/remix/references/routing-and-controllers.md +391 -0
  74. package/template/.agents/skills/remix/references/testing-patterns.md +172 -0
  75. package/{bootstrap → template}/AGENTS.md +13 -8
  76. package/template/README.md +29 -0
  77. package/template/app/actions/controller.tsx +18 -0
  78. package/template/app/assets/entry.ts +8 -0
  79. package/template/app/assets/prompt-button.tsx +163 -0
  80. package/template/app/assets.ts +20 -0
  81. package/template/app/middleware/render.tsx +43 -0
  82. package/template/app/router.ts +20 -0
  83. package/template/app/routes.ts +6 -0
  84. package/template/app/ui/document.tsx +33 -0
  85. package/template/app/ui/scaffold-home-page.tsx +527 -0
  86. package/template/gitignore +4 -0
  87. package/{bootstrap → template}/package.json +5 -6
  88. package/template/public/favicon.svg +11 -0
  89. package/{bootstrap → template}/server.ts +4 -5
  90. package/{bootstrap → template}/tsconfig.json +3 -3
  91. package/bootstrap/README.md +0 -27
  92. package/bootstrap/app/controllers/auth.tsx +0 -21
  93. package/bootstrap/app/controllers/home.tsx +0 -26
  94. package/bootstrap/app/router.ts +0 -10
  95. package/bootstrap/app/routes.ts +0 -6
  96. package/bootstrap/app/ui/document.tsx +0 -21
  97. package/bootstrap/app/ui/layout.tsx +0 -22
  98. package/bootstrap/app/utils/render.tsx +0 -7
  99. package/dist/lib/commands/skills.d.ts +0 -6
  100. package/dist/lib/commands/skills.d.ts.map +0 -1
  101. package/dist/lib/commands/skills.js +0 -222
  102. package/dist/lib/skills-cache.d.ts +0 -19
  103. package/dist/lib/skills-cache.d.ts.map +0 -1
  104. package/dist/lib/skills-cache.js +0 -89
  105. package/dist/lib/skills.d.ts +0 -30
  106. package/dist/lib/skills.d.ts.map +0 -1
  107. package/dist/lib/skills.js +0 -441
  108. package/src/lib/commands/skills.ts +0 -306
  109. package/src/lib/skills-cache.ts +0 -140
  110. package/src/lib/skills.ts +0 -706
@@ -0,0 +1,588 @@
1
+ ---
2
+ name: remix
3
+ description: Build and review Remix 3 applications using the `remix` npm package and subpath imports. Use when working on Remix app structure, routes, controllers, middleware, validation, data access, auth, sessions, file uploads, server setup, UI components, hydration, navigation, or tests.
4
+ ---
5
+
6
+ # Build a Remix App
7
+
8
+ Use this skill for end-to-end Remix app work. This skill helps you choose the right layer
9
+ first, reach for the right package, and avoid the most common Remix-specific mistakes.
10
+
11
+ ## Full Package Documentation
12
+
13
+ This skill is the quick guide. When you need fuller API documentation, examples, or package-specific
14
+ details for a `remix/*` subpath, first look for a README next to the relevant generated source file
15
+ in the published `remix` package:
16
+ `node_modules/remix/src/<subpath>/README.md`. If that README does not exist, look for the nearest
17
+ parent README because some subpaths share their parent package documentation.
18
+
19
+ Examples:
20
+
21
+ - `remix/router` -> `node_modules/remix/src/fetch-router/README.md`
22
+ - `remix/ui/button` -> `node_modules/remix/src/ui/button/README.md`
23
+
24
+ ## What Remix Is
25
+
26
+ Remix 3 is a server-first web framework built on Web APIs such as `Request`, `Response`, `URL`,
27
+ and `FormData`. All packages ship from a single npm package, `remix`, and are imported via
28
+ subpath. There is no top-level `remix` import.
29
+
30
+ A Remix app has four main pieces:
31
+
32
+ - **Routes** in `app/routes.ts` define the typed URL contract and power `href()` generation.
33
+ - **Controllers** in `app/actions` implement that contract and return `Response` objects.
34
+ - **Middleware** composes request lifecycle behavior and populates typed context via
35
+ `context.set(Key, value)`.
36
+ - **Components** render UI with `remix/ui`. This is not React. A component receives a
37
+ `handle`, reads current props from `handle.props`, and returns a render function.
38
+
39
+ ## When To Use This Skill
40
+
41
+ Use this skill for:
42
+
43
+ - new features or refactors that touch routing, controllers, middleware, data, auth, sessions, UI,
44
+ or tests
45
+ - reviewing Remix app code for correctness, architecture, or framework usage
46
+ - answering "how should this be structured in Remix?" questions
47
+ - finding the right package, reference doc, or default pattern for a task
48
+
49
+ ## Load Only The References You Need
50
+
51
+ Classify the task first, then load the smallest useful reference set. Each reference file starts
52
+ with a "What This Covers" section that lists the topics inside it — read that first to confirm
53
+ the file is relevant before reading the rest.
54
+
55
+ Use the table below to find candidates. Loading more than two or three files at once is usually a
56
+ sign that the task hasn't been narrowed enough yet.
57
+
58
+ | Task involves... | Start with |
59
+ | ----------------------------------------------------------------------------- | ------------------------------------------- |
60
+ | Defining URLs, writing controllers and actions, returning responses | `references/routing-and-controllers.md` |
61
+ | Composing the request lifecycle, ordering middleware, bridging to a server | `references/middleware-and-server.md` |
62
+ | Compiling and serving browser modules, asset URL namespaces, preloads | `references/assets-and-browser-modules.md` |
63
+ | Parsing input, validating with schemas, defining tables, querying, migrations | `references/data-and-validation.md` |
64
+ | Per-browser state, login flows, route protection, identity | `references/auth-and-sessions.md` |
65
+ | Component setup, state, lifecycle, updates, `queueTask`, context | `references/component-model.md` |
66
+ | Event handlers, styles, refs, click/key behavior, simple animations | `references/mixins-styling-events.md` |
67
+ | `clientEntry`, `run`, `<Frame>`, navigation, `<head>` | `references/hydration-frames-navigation.md` |
68
+ | Router tests, component tests, test isolation | `references/testing-patterns.md` |
69
+ | Spring physics, tweens, layout transitions | `references/animate-elements.md` |
70
+ | Authoring custom reusable mixins | `references/create-mixins.md` |
71
+
72
+ Common bundles:
73
+
74
+ - **Form or CRUD feature** -> routing, data and validation, testing; add auth if user-specific
75
+ - **Protected area** -> auth and sessions, routing, testing
76
+ - **Interactive widget** -> component model, mixins and styling; add hydration only if it runs in
77
+ the browser
78
+ - **Browser asset pipeline** -> assets and browser modules, hydration, middleware and server
79
+ - **File upload** -> middleware and server, data and validation, testing
80
+ - **Navigation or frames** -> hydration, frames, navigation
81
+
82
+ ## Default Workflow
83
+
84
+ 1. **Classify the change.** Decide whether it changes the route contract, request lifecycle, data
85
+ model, auth or session behavior, or only UI.
86
+ 2. **Start from the server contract.** Add or update `app/routes.ts` before wiring handlers or UI.
87
+ 3. **Put code in the narrowest owner.** Favor route-local code first, then promote only when reuse
88
+ is real.
89
+ 4. **Make the server path correct before adding browser behavior.** A route should return the right
90
+ `Response` via `router.fetch(...)` before you add `clientEntry(...)`, animations, or DOM
91
+ effects.
92
+ 5. **Add middleware deliberately.** Keep fast-exit middleware early and request-enriching
93
+ middleware later. Export a typed `AppContext` from the root middleware stack and use it in
94
+ controllers.
95
+ 6. **Validate input at the boundary.** Parse and validate `Request`, `FormData`, params, cookies,
96
+ and external payloads before they reach rendering or persistence logic.
97
+ 7. **Hydrate only when necessary.** Prefer server-rendered UI. Use `clientEntry(...)` and `run(...)`
98
+ only for real browser interactivity or browser-only APIs.
99
+ 8. **Test the narrowest meaningful layer.** Prefer router tests for route behavior. Use component
100
+ tests when the behavior is truly interactive or DOM-specific.
101
+ 9. **Finish with verification.** Re-read the route flow, confirm auth and authorization boundaries,
102
+ and run the smallest relevant test and typecheck loop.
103
+
104
+ ## Project Layout
105
+
106
+ Use these root directories consistently:
107
+
108
+ - `app/` for runtime application code
109
+ - `db/` for migrations and local database files
110
+ - `public/` for static assets served as-is
111
+ - `test/` for shared helpers, fixtures, and integration coverage
112
+ - `tmp/` for uploads, caches, local session files, and other scratch data
113
+
114
+ Inside `app/`, organize by responsibility:
115
+
116
+ - `assets/` for client entrypoints and client-owned browser behavior
117
+ - `actions/` for controller-owned route handlers, route-local response rendering, and route-local
118
+ UI/helpers that are not shared across route areas
119
+ - `data/` for schema, queries, persistence setup, migrations, and runtime data initialization
120
+ - `middleware/` for request lifecycle concerns such as auth, sessions, uploads, and database
121
+ injection
122
+ - `ui/` for shared cross-route UI primitives
123
+ - `utils/` only for genuinely cross-layer helpers that do not clearly belong elsewhere
124
+ - `routes.ts` for the route contract
125
+ - `router.ts` for router setup and wiring
126
+
127
+ ### Placement Precedence
128
+
129
+ When code could live in multiple places:
130
+
131
+ 1. Put it in the narrowest owner first.
132
+ 2. If it belongs to one route, keep it with that route.
133
+ 3. If it is shared UI across route areas, move it to `app/ui/`.
134
+ 4. If it is request lifecycle setup, keep it in `app/middleware/`.
135
+ 5. If it is schema, query, persistence, or startup data logic, keep it in `app/data/`.
136
+ 6. Use `app/utils/` only as a last resort for truly cross-layer helpers.
137
+
138
+ ### Route Ownership
139
+
140
+ - Put top-level leaf actions in `app/actions/controller.tsx`
141
+ - A controller's `actions` object contains only direct leaf route keys from the route map passed to
142
+ `router.map(...)`
143
+ - Add `app/actions/<route-key>/controller.tsx` for each nested route map that needs actions or
144
+ middleware, and map it explicitly with `router.map(routes.<routeKey>, controller)`
145
+ - Name directories under `app/actions/` after route-map keys, not URL path segments
146
+ - Keep route-local UI and helpers next to the controller that owns them
147
+ - Move shared cross-route UI to `app/ui/`
148
+ - If a top-level leaf grows into a route map, move its handler into the nested route-key
149
+ controller and update `app/router.ts` to map that route map explicitly
150
+
151
+ ### Response Rendering And Utilities
152
+
153
+ - Treat response rendering as action-layer code: modules that return `Response`, choose HTTP status
154
+ or headers, call `redirect(...)`, or call the local `render(...)` helper belong in `app/actions`
155
+ - Keep `app/actions/render.tsx` small; it should adapt `remix/ui/server` output to
156
+ `createHtmlResponse(...)`. Route-specific response assembly can live in flat action modules, but
157
+ directories under `app/actions/` must still match route-map keys
158
+ - Put pure support code in focused `app/utils/<topic>.ts` modules. Formatting, MIME
159
+ classification, path parsing, sorting, and normalization should be testable without a router,
160
+ request context, or `Response`, and should not import from `app/actions`, `remix/ui/server`, or
161
+ `remix/response/*`
162
+ - Do not introduce page-data intermediary shapes only to keep route-specific renderers away from
163
+ `render(...)`; keep response assembly in actions and extract only the pure helpers
164
+
165
+ ### Layout Anti-Patterns
166
+
167
+ - Do not create `app/lib/` as a generic dumping ground
168
+ - Do not create `app/components/` as a second shared UI bucket when `app/ui/` already owns that
169
+ role
170
+ - Do not create `app/controllers/`; Remix app route handlers live under `app/actions/`
171
+ - Do not put shared cross-route UI in `app/actions/`
172
+ - Do not create standalone root action files; put root route actions in `app/actions/controller.tsx`
173
+ - Do not put nested route-map keys in a controller's `actions`
174
+ - Do not register normal app leaf routes directly in `app/router.ts` when they belong in a
175
+ controller
176
+ - Do not rely on middleware from one controller to protect another controller; map middleware
177
+ explicitly in each controller that needs it
178
+ - Do not put middleware or persistence helpers in `app/utils/` when they have a clearer home
179
+
180
+ ## Core Remix Rules
181
+
182
+ - Import from `remix/<subpath>`, never `import { ... } from 'remix'`
183
+ - Treat `app/routes.ts` as the source of truth for URLs. Use `routes.<name>.href(...)` for
184
+ redirects, links, tests, and internal URL construction
185
+ - Controllers should return explicit `Response` objects, including redirects, 404s, and
186
+ validation failures. At the route boundary, prefer returning a `Response` for expected outcomes
187
+ (validation errors, conflicts, not found) over throwing for control flow
188
+ - `router.map(routes, controller)` maps only the direct leaf routes in `routes`; nested route maps
189
+ must be mapped with their own explicit controllers
190
+ - Model HTTP behavior explicitly. Status codes, headers, redirects, cache rules, and content types
191
+ are part of the route contract
192
+ - Make the server route correct first. A POST should already return the right HTML, redirect, or
193
+ error response on its own before `clientEntry(...)` layers interactivity on top
194
+ - Validate input at the boundary using `remix/data-schema` (and `remix/data-schema/form-data` for
195
+ forms). `parseSafe` makes the failure path a return value instead of an exception
196
+ - Derive `AppContext` from the root middleware stack so `get(Database)`, `get(Session)`,
197
+ `get(Auth)`, and similar keys stay typed. If the controller never reads from context, it doesn't
198
+ need the harness
199
+ - Outside actions and controllers, only use `getContext()` when `asyncContext()` is in the
200
+ middleware stack
201
+ - Remix Component is not React: read props from `handle.props`, keep state in setup-scope
202
+ variables, call `handle.update()` explicitly, and do DOM-sensitive work in event handlers or
203
+ `queueTask(...)`, not in render
204
+ - Prefer host-element mixins via `mix={mixin(...)}` for behavior and styling instead of inventing
205
+ custom host prop conventions. Use `mix={[...]}` only when composing multiple mixins
206
+ - Hydrated `clientEntry(...)` props must be serializable. Do not pass functions, class instances, or
207
+ opaque runtime objects
208
+
209
+ ## Security And Session Defaults
210
+
211
+ - Never ship demo secrets. In non-test environments, require session and provider secrets from the
212
+ environment and fail fast if they are missing
213
+ - Use hardened cookies: `httpOnly` always, `sameSite` by default, and `secure` when serving over
214
+ HTTPS
215
+ - Regenerate session IDs on login, logout, and privilege changes
216
+ - Use `requireAuth()` to protect authenticated route areas, but still authorize resource ownership
217
+ inside handlers and data writes
218
+ - Add CSRF protection when browser forms mutate state using cookie-backed sessions
219
+ - Add CORS only for endpoints that must be called cross-origin. Prefer same-origin by default
220
+ - Prefer JSX or `remix/html-template` for HTML generation so escaping stays correct
221
+ - Validate uploads for size, type, and destination. Treat filenames and content as untrusted input
222
+
223
+ ## Testing Defaults
224
+
225
+ - Prefer server and router tests first. Drive the app with `router.fetch(new Request(...))` and
226
+ assert on the returned `Response`
227
+ - Keep controller tests shaped like controllers: root route behavior belongs in
228
+ `app/actions/controller.test.ts(x)`, and nested route-map behavior belongs beside that route-key
229
+ controller
230
+ - Build a fresh router per test or per suite so sessions, in-memory storage, and database state
231
+ stay isolated
232
+ - Use `routes.<name>.href(...)` in tests so URLs stay coupled to the route contract
233
+ - For auth or session scenarios, use a test cookie and `createMemorySessionStorage()` instead of
234
+ production storage
235
+ - Co-locate tests for pure `app/utils` helpers beside their modules. Test response behavior through
236
+ router or controller tests
237
+ - Use component tests only for interactive or DOM-specific behavior. Render with `createRoot(...)`,
238
+ interact with the real DOM, and call `root.flush()` between steps
239
+ - Prefer one representative behavior test over many repetitive assertion variants
240
+
241
+ ## Common Mistakes To Avoid
242
+
243
+ - Treating Remix Component like React and reaching for hooks or implicit rerendering
244
+ - Importing from a top-level `remix` entry instead of a subpath
245
+ - Adding `clientEntry(...)` before the server-rendered route behavior is correct
246
+ - Passing non-serializable props into `clientEntry(...)`
247
+ - Calling `getContext()` without `asyncContext()` in the middleware stack
248
+ - Getting middleware order wrong; fast exits like static files belong early, request enrichment later
249
+ - Skipping boundary validation and trusting raw `FormData`, params, cookies, or external payloads
250
+ - Letting route-local domain errors leak out of the controller. Translate expected outcomes
251
+ (validation, conflicts, not-found) into the HTTP `Response` the route means to return rather than
252
+ throwing a custom `Error` subclass and catching it elsewhere
253
+ - Reaching for `createCookie` when a tamper-sensitive or server-managed per-browser fact really
254
+ wants `remix/session`. If editing the value would be a bug, use a session
255
+ - Building a JSON-only RPC layer when a normal form POST, redirect, or resource route would be
256
+ simpler. Fetch-from-the-client is a layer on top of sound route behavior, not a replacement for
257
+ it
258
+ - Treating JSON state endpoints and `<Frame>` reloads as mutually exclusive patterns. Pick the
259
+ lightest sync mechanism that fits the UX; small widgets may reasonably poll a JSON endpoint
260
+ - Assuming authentication is enough without per-resource authorization checks
261
+ - Dropping shared code into vague buckets like `utils.ts`, `helpers.ts`, or `common.ts` when
262
+ ownership is known
263
+ - Recreating the old `app/controllers` or standalone root action file layout instead of using
264
+ controllers under `app/actions`
265
+ - Putting nested route-map keys inside a controller `actions` object. Map nested route maps
266
+ explicitly in `app/router.ts`
267
+ - Treating direct `router.get(...)`/`router.post(...)` registrations as the default app structure
268
+ instead of using controllers
269
+ - Assuming controller middleware applies to controllers registered for nested route maps
270
+ - Writing only component tests for a feature whose main behavior is really an HTTP route concern
271
+
272
+ ## Package Map
273
+
274
+ Use this map to find the right package quickly. Each entry says what the package is for, not just
275
+ what it exports. Open the linked reference file when you need full examples.
276
+
277
+ ### Routing, Server, and Responses
278
+
279
+ - `remix/router` — the router itself. Use for `createRouter`, controller and middleware
280
+ types, and registering routes
281
+ - `remix/routes` — declarative route builders. Use for `route`, `get`, `post`, `put`, `del`,
282
+ `form`, `resources` when defining `app/routes.ts`
283
+ - `remix/node-fetch-server` — default Node adapter for new apps. Use `createRequestListener` with
284
+ `node:http`, `node:https`, or `node:http2` in `server.ts` when booting the template-style app
285
+ - `remix/node-serve` — managed high-performance Node server. Use `serve` when you want Remix to
286
+ manage the server lifecycle, TLS options, or uWebSockets.js setup around a Fetch handler
287
+ - `remix/assets` — browser asset server. Use for `createAssetServer` when serving compiled
288
+ scripts and styles, getting public hrefs, and emitting preloads. Configure a `basePath`, and
289
+ keep `fileMap` URL patterns relative to it. Shared compiler options such as `target`,
290
+ `sourceMaps`, `sourceMapSourcePaths`, and `minify` live at the top level
291
+ - `remix/headers` — `SuperHeaders` plus typed header parsers and builders. Use the default export
292
+ when you want a `Headers` subclass with typed accessors like `headers.contentType`,
293
+ `headers.cacheControl`, and `headers.setCookie`; use named classes such as `CacheControl`,
294
+ `ContentDisposition`, and `Vary` when working with individual header values
295
+ - `remix/response/redirect` — `redirect(href, status?)`. Use for the canonical "POST then redirect"
296
+ pattern and other location changes
297
+ - `remix/response/html` — `createHtmlResponse`. Use when you need an HTML `Response` from a string
298
+ or stream without rendering through `remix/ui`
299
+ - `remix/response/compress` — `compressResponse`. Use when compressing one-off responses outside
300
+ the global `compression()` middleware
301
+ - `remix/response/file` — file-download responses. Use for `Content-Disposition: attachment`
302
+ responses
303
+ - `remix/route-pattern` — low-level URL matching and generation. Use `RoutePattern` or
304
+ `createMatcher` when working with raw patterns outside the router. `href(...)` encodes pathname
305
+ and search params for you, and `match(...)` returns decoded params
306
+ - `remix/route-pattern/specificity` — pattern ranking helpers. Use only when building custom
307
+ matcher or reporting logic outside the normal router/matcher APIs
308
+ - `remix/fetch-proxy` — Fetch-based HTTP proxying. Use to forward a request to another origin; pass
309
+ `xForwardedHeaders` when the upstream needs forwarded proto, host, and port. It also rewrites
310
+ proxied `Set-Cookie` domain/path attributes by default
311
+
312
+ ### Data, Validation, and Persistence
313
+
314
+ - `remix/data-schema` — schema builders for runtime validation. Use for `parse` and `parseSafe`
315
+ to validate any input that crosses a trust boundary, and `.transform(...)` when validated output
316
+ should map to a different value or type
317
+ - `remix/data-schema/checks` — common check helpers (`email`, `minLength`, `maxLength`, etc.).
318
+ Use to compose into a schema
319
+ - `remix/data-schema/coerce` — coercion helpers for strings, numbers, booleans, dates, and ids.
320
+ Use when input arrives as a string but should be a typed value
321
+ - `remix/data-schema/form-data` — `f.object` and `f.field` for parsing `FormData` directly. Use
322
+ in actions that read browser forms
323
+ - `remix/data-schema/lazy` — recursive or mutually-referential schemas. Use when a schema needs to
324
+ refer to itself or another schema that is declared later
325
+ - `remix/data-table` — typed tables and a `Database` interface. Use for `table`, `column`,
326
+ `createDatabase` when modeling persisted data
327
+ - `remix/data-table/sqlite`, `remix/data-table/postgres`, `remix/data-table/mysql` — adapters.
328
+ Use to back `createDatabase` with a real engine. SQLite accepts Node, Bun, and compatible
329
+ synchronous clients with the shared `prepare`/`exec` surface
330
+ - `remix/data-table/migrations` — migration authoring and runners. Use for `createMigration`,
331
+ `createMigrationRunner`
332
+ - `remix/data-table/migrations/node` — `loadMigrations` from disk. Use in startup scripts that
333
+ apply migrations
334
+ - `remix/data-table/operators` — query operators such as `inList(...)`. Use when `where` clauses
335
+ need set or comparison logic
336
+ - `remix/data-table/sql-helpers` — SQL helper utilities for adapter or advanced query work. Avoid
337
+ this in normal app code unless you are intentionally working below the table/query API
338
+
339
+ ### Auth, Sessions, and Cookies
340
+
341
+ - `remix/session` — the `Session` object: `get`, `set`, `flash`, `unset`, `regenerateId`. Use for
342
+ any per-browser state where tampering would be a bug (login, "I submitted this form already",
343
+ cart, flash messages)
344
+ - `remix/middleware/session` — `session(cookie, storage)`. Use to wire a session cookie and
345
+ storage backend into the root middleware stack
346
+ - `remix/session-storage/fs`, `remix/session-storage/memory`, `remix/session-storage/cookie` —
347
+ storage backends. Use `fs-storage` for single-process apps, `memory-storage` for tests,
348
+ `cookie-storage` for stateless deployments where data fits in a cookie
349
+ - `remix/session-storage/redis` — Redis-backed storage. Use for multi-process or multi-host
350
+ deployments
351
+ - `remix/session-storage/memcache` — Memcache-backed storage. Same multi-host use case as Redis
352
+ - `remix/cookie` — `createCookie` for plain signed/unsigned cookies. Use for non-sensitive
353
+ preferences where the client is allowed to control the value (theme, locale, dismissed banner).
354
+ For state where tampering matters, prefer `remix/session`
355
+ - `remix/auth` — credentials, OAuth, OIDC, and Atmosphere providers. Use to define how identity is
356
+ verified, start/finish external login, and refresh stored OAuth/OIDC token bundles with
357
+ `refreshExternalAuth(...)`
358
+ - `remix/middleware/auth` — `auth({ schemes })`, `requireAuth`, the `Auth` context key. Use to
359
+ resolve identity into the request context and to gate routes
360
+
361
+ ### UI, Hydration, and Browser Behavior
362
+
363
+ - `remix/ui` — the component runtime: components, core mixins, `clientEntry`, `run`, `<Frame>`,
364
+ navigation helpers, and `createRoot`. Use for app UI behavior
365
+ - `remix/ui/server` — server rendering: `renderToStream`, `renderToString`. Use in the
366
+ `app/actions/render.tsx` helper that returns HTML responses
367
+ - `remix/ui/animation` — animation APIs: `animateEntrance`, `animateExit`, `animateLayout`,
368
+ `spring`, `tween`, and `easings`
369
+ - `remix/ui/<primitive>` — UI primitives, mixins, glyphs, and theme helpers. Current subpaths
370
+ include `remix/ui/accordion`, `remix/ui/anchor`, `remix/ui/breadcrumbs`, `remix/ui/button`,
371
+ `remix/ui/combobox`, `remix/ui/glyph`, `remix/ui/listbox`, `remix/ui/menu`,
372
+ `remix/ui/popover`, `remix/ui/scroll-lock`, `remix/ui/select`, `remix/ui/separator`, and
373
+ `remix/ui/theme`
374
+ - `remix/ui/test` — component test rendering helpers such as `render`
375
+ - `remix/ui/jsx-runtime` and `remix/ui/jsx-dev-runtime` — JSX transform targets. Configured in
376
+ `tsconfig.json`, rarely imported directly
377
+ - `remix/html-template` — escaped HTML template literals. Use when generating HTML outside the
378
+ component system (RSS feeds, email bodies, error pages)
379
+ - `remix/file-storage` — backend-agnostic `File` storage interface. Use as the type bound for
380
+ upload destinations
381
+ - `remix/file-storage/fs`, `remix/file-storage/memory`, `remix/file-storage/s3` — storage
382
+ backends. Use to implement an upload destination
383
+
384
+ ### Middleware
385
+
386
+ - `remix/middleware/static` — `staticFiles(dir)`. Use to serve files from `public/` exactly as
387
+ they exist on disk
388
+ - `remix/middleware/form-data` — `formData()`. Use to parse `FormData` once and expose it via
389
+ `get(FormData)` instead of calling `await request.formData()` in each action
390
+ - `remix/form-data-parser` — lower-level `parseFormData`, `FileUpload`. Use when implementing
391
+ custom upload handlers. Upload handler errors propagate directly
392
+ - `remix/multipart-parser` and `remix/multipart-parser/node` — low-level multipart stream parsing.
393
+ `MultipartPart.headers` is a plain object keyed by lower-case header name; read values with
394
+ bracket notation such as `part.headers['content-type']`
395
+ - `remix/middleware/compression` — `compression()`. Use globally for text-like responses
396
+ - `remix/middleware/logger` — `logger()`. Use in development for request logs; pass `colors` to
397
+ force terminal color output on or off
398
+ - `remix/middleware/method-override` — `methodOverride()`. Use when HTML forms need `PUT`,
399
+ `PATCH`, or `DELETE`
400
+ - `remix/middleware/async-context` — `asyncContext()`, `getContext()`. Use when helpers outside
401
+ actions need request context without threading it through every call
402
+ - `remix/middleware/cors` — `cors(opts?)`. Use for endpoints called cross-origin
403
+ - `remix/middleware/csrf` — `csrf(opts?)`. Use when session-backed forms mutate state and need
404
+ synchronizer-token CSRF protection
405
+ - `remix/middleware/cop` — cross-origin protection. Use to reject unsafe cross-origin browser
406
+ requests
407
+
408
+ ### Test
409
+
410
+ - `remix/test` — `describe`, `it`, and lifecycle hooks. Use as the test framework
411
+ - `remix/test/cli` — programmatic test runner APIs such as `runRemixTest`
412
+ - `remix/node-fetch-server/test` — `createTestServer` for end-to-end tests that need a real local
413
+ HTTP server around a Fetch handler
414
+ - `remix/cli` — programmatic Remix CLI API. Use the `remix` executable for project commands such
415
+ as `remix test`, `remix routes`, `remix doctor`, and `remix version`
416
+ - `remix/assert` — assertion helpers. Use in place of `node:assert` so messages render cleanly
417
+ in the runner
418
+ - `remix/terminal` — ANSI styles, color detection, style factories, and testable terminal streams.
419
+ Use for CLIs and terminal output instead of hand-rolled escape sequences
420
+ - `remix/fs` — small filesystem helpers such as `openLazyFile` and `writeFile`. Use in Node-only
421
+ app or tooling code when you need lazy file responses or safe file writes
422
+ - `remix/lazy-file` — `LazyFile` primitives and byte-range helpers. Use when implementing file or
423
+ range responses below the higher-level response/file helpers
424
+ - `remix/mime` — content-type and MIME detection helpers. Use instead of maintaining app-local
425
+ extension maps
426
+ - `remix/tar-parser` — streaming tar parsing. Use for import/export tooling that consumes tar
427
+ archives
428
+
429
+ ## Canonical Patterns
430
+
431
+ ### Define routes first
432
+
433
+ ```typescript
434
+ import { form, get, post, resources, route } from 'remix/routes'
435
+
436
+ export const routes = route({
437
+ home: '/',
438
+ contact: form('contact'),
439
+ books: {
440
+ index: '/books',
441
+ show: '/books/:slug',
442
+ },
443
+ auth: route('auth', {
444
+ login: form('login'),
445
+ logout: post('logout'),
446
+ }),
447
+ admin: route('admin', {
448
+ index: get('/'),
449
+ books: resources('books', { param: 'bookId' }),
450
+ }),
451
+ })
452
+ ```
453
+
454
+ ### Type controllers against the route contract
455
+
456
+ ```typescript
457
+ import { createController } from 'remix/router'
458
+
459
+ import { routes } from '../routes.ts'
460
+
461
+ export default createController(routes.books, {
462
+ actions: {
463
+ async index({ get }) {
464
+ let db = get(Database)
465
+ let allBooks = await db.findMany(books, { orderBy: ['id', 'asc'] })
466
+ return render(<BooksIndexPage allBooks={allBooks} />)
467
+ },
468
+ async show({ get, params }) {
469
+ let db = get(Database)
470
+ let book = await db.findOne(books, { where: { slug: params.slug } })
471
+ if (!book) return new Response('Not Found', { status: 404 })
472
+ return render(<BookShowPage book={book} />)
473
+ },
474
+ },
475
+ })
476
+ ```
477
+
478
+ ### Register Controllers Explicitly
479
+
480
+ ```typescript
481
+ import { createRouter } from 'remix/router'
482
+
483
+ import rootController from './actions/controller.tsx'
484
+ import adminController from './actions/admin/controller.tsx'
485
+ import adminBooksController from './actions/admin/books/controller.tsx'
486
+ import authController from './actions/auth/controller.tsx'
487
+ import authLoginController from './actions/auth/login/controller.tsx'
488
+ import booksController from './actions/books/controller.tsx'
489
+ import contactController from './actions/contact/controller.tsx'
490
+ import { routes } from './routes.ts'
491
+
492
+ export const router = createRouter({ middleware })
493
+
494
+ router.map(routes, rootController)
495
+ router.map(routes.contact, contactController)
496
+ router.map(routes.books, booksController)
497
+ router.map(routes.auth, authController)
498
+ router.map(routes.auth.login, authLoginController)
499
+ router.map(routes.admin, adminController)
500
+ router.map(routes.admin.books, adminBooksController)
501
+ ```
502
+
503
+ ### Compose middleware deliberately
504
+
505
+ ```typescript
506
+ import { createRouter } from 'remix/router'
507
+
508
+ let middleware = []
509
+
510
+ if (process.env.NODE_ENV === 'development') {
511
+ middleware.push(logger())
512
+ }
513
+
514
+ middleware.push(compression())
515
+ middleware.push(staticFiles('./public'))
516
+ middleware.push(formData())
517
+ middleware.push(methodOverride())
518
+ middleware.push(session(cookie, storage))
519
+ middleware.push(asyncContext())
520
+ middleware.push(loadDatabase())
521
+ middleware.push(loadAuth())
522
+
523
+ let router = createRouter({ middleware })
524
+ ```
525
+
526
+ ### Validate, mutate, and respond
527
+
528
+ ```typescript
529
+ import { createController } from 'remix/router'
530
+ import { redirect } from 'remix/response/redirect'
531
+ import * as s from 'remix/data-schema'
532
+ import * as f from 'remix/data-schema/form-data'
533
+ import { Session } from 'remix/session'
534
+ import { Database } from 'remix/data-table'
535
+
536
+ import { routes } from '../routes.ts'
537
+
538
+ let bookSchema = f.object({
539
+ slug: f.field(s.string()),
540
+ title: f.field(s.string()),
541
+ })
542
+
543
+ export default createController(routes.books, {
544
+ actions: {
545
+ async create({ get }) {
546
+ let parsed = s.parseSafe(bookSchema, get(FormData))
547
+ if (!parsed.success) {
548
+ return render(<NewBookPage errors={parsed.issues} />, { status: 400 })
549
+ }
550
+
551
+ let db = get(Database)
552
+ let book = await db.create(books, parsed.value)
553
+
554
+ let session = get(Session)
555
+ session.flash('message', `Added ${book.title}.`)
556
+
557
+ return redirect(routes.books.show.href({ slug: book.slug }))
558
+ },
559
+ },
560
+ })
561
+ ```
562
+
563
+ This shape works without JavaScript, returns a `Response` for every outcome, and is ready for
564
+ `clientEntry(...)` interactivity when the UI needs it.
565
+
566
+ ### Build UI from handle props plus render
567
+
568
+ ```tsx
569
+ import { on, type Handle } from 'remix/ui'
570
+
571
+ function Counter(handle: Handle<{ initialCount?: number; label: string }>) {
572
+ let count = handle.props.initialCount ?? 0
573
+
574
+ return () => (
575
+ <button
576
+ mix={on('click', () => {
577
+ count++
578
+ handle.update()
579
+ })}
580
+ >
581
+ {handle.props.label}: {count}
582
+ </button>
583
+ )
584
+ }
585
+ ```
586
+
587
+ Only add `clientEntry(...)` and `run(...)` when the component needs browser interactivity or
588
+ browser-only APIs.