@remix-run/cli 0.3.0 → 0.3.2
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.
- package/dist/lib/bootstrap-project.js +7 -4
- package/dist/lib/help-text.js +1 -1
- package/package.json +4 -4
- package/src/lib/bootstrap-project.ts +9 -6
- package/src/lib/help-text.ts +3 -2
- package/template/.agents/skills/remix/SKILL.md +115 -264
- package/template/.agents/skills/remix/references/animate-elements.md +8 -17
- package/template/.agents/skills/remix/references/assets-and-browser-modules.md +14 -33
- package/template/.agents/skills/remix/references/auth-and-sessions.md +10 -31
- package/template/.agents/skills/remix/references/component-model.md +13 -25
- package/template/.agents/skills/remix/references/create-mixins.md +4 -9
- package/template/.agents/skills/remix/references/data-and-validation.md +54 -77
- package/template/.agents/skills/remix/references/hydration-frames-navigation.md +23 -56
- package/template/.agents/skills/remix/references/middleware-and-server.md +17 -39
- package/template/.agents/skills/remix/references/mixins-styling-events.md +14 -32
- package/template/.agents/skills/remix/references/routing-and-controllers.md +17 -43
- package/template/.agents/skills/remix/references/testing-patterns.md +13 -32
- package/template/app/middleware/render.tsx +2 -1
- package/template/app/ui/document.tsx +22 -17
- package/template/app/ui/scaffold-home-page.tsx +56 -47
- package/template/server.ts +3 -1
|
@@ -5,16 +5,11 @@ description: Build and review Remix 3 applications using the `remix` npm package
|
|
|
5
5
|
|
|
6
6
|
# Build a Remix App
|
|
7
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.
|
|
8
|
+
Use this skill for end-to-end Remix app work. This skill helps you choose the right layer first, reach for the right package, and avoid the most common Remix-specific mistakes.
|
|
10
9
|
|
|
11
10
|
## Full Package Documentation
|
|
12
11
|
|
|
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.
|
|
12
|
+
This skill is the quick guide. When you need fuller API documentation, examples, or package-specific details for a `remix/*` subpath, first look for a README next to the relevant generated source file in the published `remix` package: `node_modules/remix/src/<subpath>/README.md`. If that README does not exist, look for the nearest parent README because some subpaths share their parent package documentation.
|
|
18
13
|
|
|
19
14
|
Examples:
|
|
20
15
|
|
|
@@ -23,37 +18,29 @@ Examples:
|
|
|
23
18
|
|
|
24
19
|
## What Remix Is
|
|
25
20
|
|
|
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.
|
|
21
|
+
Remix 3 is a server-first web framework built on Web APIs such as `Request`, `Response`, `URL`, and `FormData`. All packages ship from a single npm package, `remix`, and are imported via subpath. There is no top-level `remix` import.
|
|
29
22
|
|
|
30
23
|
A Remix app has four main pieces:
|
|
31
24
|
|
|
32
25
|
- **Routes** in `app/routes.ts` define the typed URL contract and power `href()` generation.
|
|
33
26
|
- **Controllers** in `app/actions` implement that contract and return `Response` objects.
|
|
34
|
-
- **Middleware** composes request lifecycle behavior and populates typed context via
|
|
35
|
-
|
|
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.
|
|
27
|
+
- **Middleware** composes request lifecycle behavior and populates typed context via `context.set(Key, value)`.
|
|
28
|
+
- **Components** render UI with `remix/ui`. This is not React. A component receives a `handle`, reads current props from `handle.props`, and returns a zero-argument render function.
|
|
38
29
|
|
|
39
30
|
## When To Use This Skill
|
|
40
31
|
|
|
41
32
|
Use this skill for:
|
|
42
33
|
|
|
43
|
-
- new features or refactors that touch routing, controllers, middleware, data, auth, sessions, UI,
|
|
44
|
-
or tests
|
|
34
|
+
- new features or refactors that touch routing, controllers, middleware, data, auth, sessions, UI, or tests
|
|
45
35
|
- reviewing Remix app code for correctness, architecture, or framework usage
|
|
46
36
|
- answering "how should this be structured in Remix?" questions
|
|
47
37
|
- finding the right package, reference doc, or default pattern for a task
|
|
48
38
|
|
|
49
39
|
## Load Only The References You Need
|
|
50
40
|
|
|
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.
|
|
41
|
+
Classify the task first, then load the smallest useful reference set. Each reference file starts with a "What This Covers" section that lists the topics inside it — read that first to confirm the file is relevant before reading the rest.
|
|
54
42
|
|
|
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.
|
|
43
|
+
Use the table below to find candidates. Loading more than two or three files at once is usually a sign that the task hasn't been narrowed enough yet.
|
|
57
44
|
|
|
58
45
|
| Task involves... | Start with |
|
|
59
46
|
| ----------------------------------------------------------------------------- | ------------------------------------------- |
|
|
@@ -73,33 +60,22 @@ Common bundles:
|
|
|
73
60
|
|
|
74
61
|
- **Form or CRUD feature** -> routing, data and validation, testing; add auth if user-specific
|
|
75
62
|
- **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
|
|
63
|
+
- **Interactive widget** -> component model, mixins and styling; add hydration only if it runs in the browser
|
|
78
64
|
- **Browser asset pipeline** -> assets and browser modules, hydration, middleware and server
|
|
79
65
|
- **File upload** -> middleware and server, data and validation, testing
|
|
80
66
|
- **Navigation or frames** -> hydration, frames, navigation
|
|
81
67
|
|
|
82
68
|
## Default Workflow
|
|
83
69
|
|
|
84
|
-
1. **Classify the change.** Decide whether it changes the route contract, request lifecycle, data
|
|
85
|
-
model, auth or session behavior, or only UI.
|
|
70
|
+
1. **Classify the change.** Decide whether it changes the route contract, request lifecycle, data model, auth or session behavior, or only UI.
|
|
86
71
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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.
|
|
72
|
+
3. **Put code in the narrowest owner.** Favor route-local code first, then promote only when reuse is real.
|
|
73
|
+
4. **Make the server path correct before adding browser behavior.** A route should return the right `Response` via `router.fetch(...)` before you add `clientEntry(...)`, animations, or DOM effects.
|
|
74
|
+
5. **Add middleware deliberately.** Keep fast-exit middleware early and request-enriching middleware later. Export a typed `AppContext` from the root middleware stack and use it in controllers.
|
|
75
|
+
6. **Validate input at the boundary.** Parse and validate `Request`, `FormData`, params, cookies, and external payloads before they reach rendering or persistence logic.
|
|
76
|
+
7. **Hydrate only when necessary.** Prefer server-rendered UI. Use `clientEntry(...)` and `run(...)` only for real browser interactivity or browser-only APIs.
|
|
77
|
+
8. **Test the narrowest meaningful layer.** Prefer router tests for route behavior. Use component tests when the behavior is truly interactive or DOM-specific.
|
|
78
|
+
9. **Finish with verification.** Re-read the route flow, confirm auth and authorization boundaries, and run the smallest relevant test and typecheck loop.
|
|
103
79
|
|
|
104
80
|
## Project Layout
|
|
105
81
|
|
|
@@ -114,11 +90,9 @@ Use these root directories consistently:
|
|
|
114
90
|
Inside `app/`, organize by responsibility:
|
|
115
91
|
|
|
116
92
|
- `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
|
|
93
|
+
- `actions/` for controller-owned route handlers, route-local response rendering, and route-local UI/helpers that are not shared across route areas
|
|
119
94
|
- `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
|
|
95
|
+
- `middleware/` for request lifecycle concerns such as auth, sessions, uploads, and database injection
|
|
122
96
|
- `ui/` for shared cross-route UI primitives
|
|
123
97
|
- `utils/` only for genuinely cross-layer helpers that do not clearly belong elsewhere
|
|
124
98
|
- `routes.ts` for the route contract
|
|
@@ -138,83 +112,53 @@ When code could live in multiple places:
|
|
|
138
112
|
### Route Ownership
|
|
139
113
|
|
|
140
114
|
- 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
|
-
|
|
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)`
|
|
115
|
+
- A controller's `actions` object contains only direct leaf route keys from the route map passed to `router.map(...)`
|
|
116
|
+
- Add `app/actions/<route-key>/controller.tsx` for each nested route map that needs actions or middleware, and map it explicitly with `router.map(routes.<routeKey>, controller)`
|
|
145
117
|
- Name directories under `app/actions/` after route-map keys, not URL path segments
|
|
146
118
|
- Keep route-local UI and helpers next to the controller that owns them
|
|
147
119
|
- 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
|
|
120
|
+
- If a top-level leaf grows into a route map, move its handler into the nested route-key controller and update `app/router.ts` to map that route map explicitly
|
|
150
121
|
|
|
151
122
|
### Response Rendering And Utilities
|
|
152
123
|
|
|
153
|
-
- Treat response rendering as action-layer code: modules that return `Response`, choose HTTP status
|
|
154
|
-
|
|
155
|
-
-
|
|
156
|
-
|
|
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
|
|
124
|
+
- Treat response rendering as action-layer code: modules that return `Response`, choose HTTP status or headers, call `redirect(...)`, or call the local `render(...)` helper belong in `app/actions`
|
|
125
|
+
- Keep `app/actions/render.tsx` small; it should adapt `remix/ui/server` output to `createHtmlResponse(...)`. Route-specific response assembly can live in flat action modules, but directories under `app/actions/` must still match route-map keys
|
|
126
|
+
- Put pure support code in focused `app/utils/<topic>.ts` modules. Formatting, MIME classification, path parsing, sorting, and normalization should be testable without a router, request context, or `Response`, and should not import from `app/actions`, `remix/ui/server`, or `remix/response/*`
|
|
127
|
+
- Do not introduce page-data intermediary shapes only to keep route-specific renderers away from `render(...)`; keep response assembly in actions and extract only the pure helpers
|
|
164
128
|
|
|
165
129
|
### Layout Anti-Patterns
|
|
166
130
|
|
|
167
131
|
- 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
|
|
132
|
+
- Do not create `app/components/` as a second shared UI bucket when `app/ui/` already owns that role
|
|
170
133
|
- Do not create `app/controllers/`; Remix app route handlers live under `app/actions/`
|
|
171
134
|
- Do not put shared cross-route UI in `app/actions/`
|
|
172
135
|
- Do not create standalone root action files; put root route actions in `app/actions/controller.tsx`
|
|
173
136
|
- 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
|
-
|
|
176
|
-
- Do not rely on middleware from one controller to protect another controller; map middleware
|
|
177
|
-
explicitly in each controller that needs it
|
|
137
|
+
- Do not register normal app leaf routes directly in `app/router.ts` when they belong in a controller
|
|
138
|
+
- Do not rely on middleware from one controller to protect another controller; map middleware explicitly in each controller that needs it
|
|
178
139
|
- Do not put middleware or persistence helpers in `app/utils/` when they have a clearer home
|
|
179
140
|
|
|
180
141
|
## Core Remix Rules
|
|
181
142
|
|
|
182
143
|
- 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
|
-
|
|
185
|
-
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
- `
|
|
189
|
-
|
|
190
|
-
-
|
|
191
|
-
|
|
192
|
-
-
|
|
193
|
-
|
|
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
|
|
144
|
+
- Treat `app/routes.ts` as the source of truth for URLs. Use `routes.<name>.href(...)` for redirects, links, tests, and internal URL construction
|
|
145
|
+
- Controllers should return explicit `Response` objects, including redirects, 404s, and validation failures. At the route boundary, prefer returning a `Response` for expected outcomes (validation errors, conflicts, not found) over throwing for control flow
|
|
146
|
+
- `router.map(routes, controller)` maps only the direct leaf routes in `routes`; nested route maps must be mapped with their own explicit controllers
|
|
147
|
+
- Model HTTP behavior explicitly. Status codes, headers, redirects, cache rules, and content types are part of the route contract
|
|
148
|
+
- Make the server route correct first. A POST should already return the right HTML, redirect, or error response on its own before `clientEntry(...)` layers interactivity on top
|
|
149
|
+
- Validate input at the boundary using `remix/data-schema` (and `remix/data-schema/form-data` for forms). `parseSafe` makes the failure path a return value instead of an exception
|
|
150
|
+
- Derive `AppContext` from the root middleware stack so `get(Database)`, `get(Session)`, `get(Auth)`, and similar keys stay typed. If the controller never reads from context, it doesn't need the harness
|
|
151
|
+
- Outside actions and controllers, only use `getContext()` when `asyncContext()` is in the middleware stack
|
|
152
|
+
- Remix Component is not React: write `function Name(handle: Handle<Props>) { return () => ... }`, read props from `handle.props`, keep state in setup-scope variables, call `handle.update()` explicitly, and do DOM-sensitive work in event handlers or `queueTask(...)`, not in render
|
|
153
|
+
- Prefer host-element mixins via `mix={mixin(...)}` for behavior and styling instead of inventing custom host prop conventions. Use `mix={[...]}` only when composing multiple mixins
|
|
154
|
+
- Hydrated `clientEntry(...)` props must be serializable. Do not pass functions, class instances, or opaque runtime objects
|
|
208
155
|
|
|
209
156
|
## Security And Session Defaults
|
|
210
157
|
|
|
211
|
-
- Never ship demo secrets. In non-test environments, require session and provider secrets from the
|
|
212
|
-
|
|
213
|
-
- Use hardened cookies: `httpOnly` always, `sameSite` by default, and `secure` when serving over
|
|
214
|
-
HTTPS
|
|
158
|
+
- Never ship demo secrets. In non-test environments, require session and provider secrets from the environment and fail fast if they are missing
|
|
159
|
+
- Use hardened cookies: `httpOnly` always, `sameSite` by default, and `secure` when serving over HTTPS
|
|
215
160
|
- 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
|
|
161
|
+
- Use `requireAuth()` to protect authenticated route areas, but still authorize resource ownership inside handlers and data writes
|
|
218
162
|
- Add CSRF protection when browser forms mutate state using cookie-backed sessions
|
|
219
163
|
- Add CORS only for endpoints that must be called cross-origin. Prefer same-origin by default
|
|
220
164
|
- Prefer JSX or `remix/html-template` for HTML generation so escaping stays correct
|
|
@@ -222,20 +166,13 @@ When code could live in multiple places:
|
|
|
222
166
|
|
|
223
167
|
## Testing Defaults
|
|
224
168
|
|
|
225
|
-
- Prefer server and router tests first. Drive the app with `router.fetch(new Request(...))` and
|
|
226
|
-
|
|
227
|
-
-
|
|
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
|
|
169
|
+
- Prefer server and router tests first. Drive the app with `router.fetch(new Request(...))` and assert on the returned `Response`
|
|
170
|
+
- Keep controller tests shaped like controllers: root route behavior belongs in `app/actions/controller.test.ts(x)`, and nested route-map behavior belongs beside that route-key controller
|
|
171
|
+
- Build a fresh router per test or per suite so sessions, in-memory storage, and database state stay isolated
|
|
232
172
|
- 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
|
-
|
|
235
|
-
-
|
|
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
|
|
173
|
+
- For auth or session scenarios, use a test cookie and `createMemorySessionStorage()` instead of production storage
|
|
174
|
+
- Co-locate tests for pure `app/utils` helpers beside their modules. Test response behavior through router or controller tests
|
|
175
|
+
- Use component tests only for interactive or DOM-specific behavior. Render with `createRoot(...)`, interact with the real DOM, and call `root.flush()` between steps
|
|
239
176
|
- Prefer one representative behavior test over many repetitive assertion variants
|
|
240
177
|
|
|
241
178
|
## Common Mistakes To Avoid
|
|
@@ -247,184 +184,100 @@ When code could live in multiple places:
|
|
|
247
184
|
- Calling `getContext()` without `asyncContext()` in the middleware stack
|
|
248
185
|
- Getting middleware order wrong; fast exits like static files belong early, request enrichment later
|
|
249
186
|
- 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
|
-
|
|
252
|
-
|
|
253
|
-
-
|
|
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
|
|
187
|
+
- Letting route-local domain errors leak out of the controller. Translate expected outcomes (validation, conflicts, not-found) into the HTTP `Response` the route means to return rather than throwing a custom `Error` subclass and catching it elsewhere
|
|
188
|
+
- Reaching for `createCookie` when a tamper-sensitive or server-managed per-browser fact really wants `remix/session`. If editing the value would be a bug, use a session
|
|
189
|
+
- Building a JSON-only RPC layer when a normal form POST, redirect, or resource route would be simpler. Fetch-from-the-client is a layer on top of sound route behavior, not a replacement for it
|
|
190
|
+
- Treating JSON state endpoints and `<Frame>` reloads as mutually exclusive patterns. Pick the lightest sync mechanism that fits the UX; small widgets may reasonably poll a JSON endpoint
|
|
260
191
|
- 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
|
-
|
|
263
|
-
-
|
|
264
|
-
|
|
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
|
|
192
|
+
- Dropping shared code into vague buckets like `utils.ts`, `helpers.ts`, or `common.ts` when ownership is known
|
|
193
|
+
- Recreating the old `app/controllers` or standalone root action file layout instead of using controllers under `app/actions`
|
|
194
|
+
- Putting nested route-map keys inside a controller `actions` object. Map nested route maps explicitly in `app/router.ts`
|
|
195
|
+
- Treating direct `router.get(...)`/`router.post(...)` registrations as the default app structure instead of using controllers
|
|
269
196
|
- Assuming controller middleware applies to controllers registered for nested route maps
|
|
270
197
|
- Writing only component tests for a feature whose main behavior is really an HTTP route concern
|
|
271
198
|
|
|
272
199
|
## Package Map
|
|
273
200
|
|
|
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.
|
|
201
|
+
Use this map to find the right package quickly. Each entry says what the package is for, not just what it exports. Open the linked reference file when you need full examples.
|
|
276
202
|
|
|
277
203
|
### Routing, Server, and Responses
|
|
278
204
|
|
|
279
|
-
- `remix/router` — the router itself. Use for `createRouter`, controller and middleware
|
|
280
|
-
|
|
281
|
-
- `remix/
|
|
282
|
-
|
|
283
|
-
- `remix/
|
|
284
|
-
|
|
285
|
-
- `remix/
|
|
286
|
-
|
|
287
|
-
- `remix/
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
|
205
|
+
- `remix/router` — the router itself. Use for `createRouter`, controller and middleware types, and registering routes
|
|
206
|
+
- `remix/routes` — declarative route builders. Use for `route`, `get`, `post`, `put`, `del`, `form`, `resources` when defining `app/routes.ts`
|
|
207
|
+
- `remix/node-fetch-server` — default Node adapter for new apps. Use `createRequestListener` with `node:http`, `node:https`, or `node:http2` in `server.ts` when booting the template-style app
|
|
208
|
+
- `remix/assets` — browser asset server. Use for `createAssetServer` when serving compiled scripts and styles, getting public hrefs, and emitting preloads. Configure a `basePath`, and keep `fileMap` URL patterns relative to it. Shared compiler options such as `target`, `sourceMaps`, `sourceMapSourcePaths`, and `minify` live at the top level
|
|
209
|
+
- `remix/headers` — `SuperHeaders` plus typed header parsers and builders. Use the default export when you want a `Headers` subclass with typed accessors like `headers.contentType`, `headers.cacheControl`, and `headers.setCookie`; use named classes such as `CacheControl`, `ContentDisposition`, and `Vary` when working with individual header values
|
|
210
|
+
- `remix/response/redirect` — `redirect(href, status?)`. Use for the canonical "POST then redirect" pattern and other location changes
|
|
211
|
+
- `remix/response/html` — `createHtmlResponse`. Use when you need an HTML `Response` from a string or stream without rendering through `remix/ui`
|
|
212
|
+
- `remix/response/compress` — `compressResponse`. Use when compressing one-off responses outside the global `compression()` middleware
|
|
213
|
+
- `remix/response/file` — file-download responses. Use for `Content-Disposition: attachment` responses
|
|
214
|
+
- `remix/route-pattern` — low-level URL matching and generation. Use `RoutePattern` or `createMatcher` when working with raw patterns outside the router. `href(...)` encodes pathname and search params for you, and `match(...)` returns decoded params
|
|
215
|
+
- `remix/route-pattern/specificity` — pattern ranking helpers. Use only when building custom matcher or reporting logic outside the normal router/matcher APIs
|
|
216
|
+
- `remix/fetch-proxy` — Fetch-based HTTP proxying. Use to forward a request to another origin; pass `xForwardedHeaders` when the upstream needs forwarded proto, host, and port. It also rewrites proxied `Set-Cookie` domain/path attributes by default
|
|
311
217
|
|
|
312
218
|
### Data, Validation, and Persistence
|
|
313
219
|
|
|
314
|
-
- `remix/data-schema` — schema builders for runtime validation. Use for `parse` and `parseSafe`
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
- `remix/data-schema/
|
|
318
|
-
|
|
319
|
-
- `remix/data-
|
|
320
|
-
|
|
321
|
-
- `remix/data-
|
|
322
|
-
|
|
323
|
-
- `remix/data-
|
|
324
|
-
|
|
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
|
|
220
|
+
- `remix/data-schema` — schema builders for runtime validation. Use for `parse` and `parseSafe` to validate any input that crosses a trust boundary, and `.transform(...)` when validated output should map to a different value or type
|
|
221
|
+
- `remix/data-schema/checks` — common check helpers (`email`, `minLength`, `maxLength`, etc.). Use to compose into a schema
|
|
222
|
+
- `remix/data-schema/coerce` — coercion helpers for strings, numbers, booleans, dates, and ids. Use when input arrives as a string but should be a typed value
|
|
223
|
+
- `remix/data-schema/form-data` — `f.object` and `f.field` for parsing `FormData` directly. Use in actions that read browser forms
|
|
224
|
+
- `remix/data-schema/lazy` — recursive or mutually-referential schemas. Use when a schema needs to refer to itself or another schema that is declared later
|
|
225
|
+
- `remix/data-table` — typed tables and a `Database` interface. Use for `table`, `column`, `createDatabase` when modeling persisted data
|
|
226
|
+
- `remix/data-table/sqlite`, `remix/data-table/postgres`, `remix/data-table/mysql` — adapters. Use to back `createDatabase` with a real engine. SQLite accepts Node, Bun, and compatible synchronous clients with the shared `prepare`/`exec` surface
|
|
227
|
+
- `remix/data-table/migrations` — migration authoring and runners. Use for `createMigration`, `createMigrationRunner`
|
|
228
|
+
- `remix/data-table/migrations/node` — `loadMigrations` from disk. Use in startup scripts that apply migrations
|
|
229
|
+
- `remix/data-table/operators` — query operators such as `inList(...)`. Use when `where` clauses need set or comparison logic
|
|
230
|
+
- `remix/data-table/sql-helpers` — SQL helper utilities for adapter or advanced query work. Avoid this in normal app code unless you are intentionally working below the table/query API
|
|
338
231
|
|
|
339
232
|
### Auth, Sessions, and Cookies
|
|
340
233
|
|
|
341
|
-
- `remix/session` — the `Session` object: `get`, `set`, `flash`, `unset`, `regenerateId`. Use for
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
- `remix/
|
|
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
|
|
234
|
+
- `remix/session` — the `Session` object: `get`, `set`, `flash`, `unset`, `regenerateId`. Use for any per-browser state where tampering would be a bug (login, "I submitted this form already", cart, flash messages)
|
|
235
|
+
- `remix/middleware/session` — `session(cookie, storage)`. Use to wire a session cookie and storage backend into the root middleware stack
|
|
236
|
+
- `remix/session-storage/fs`, `remix/session-storage/memory`, `remix/session-storage/cookie` — storage backends. Use `fs-storage` for single-process apps, `memory-storage` for tests, `cookie-storage` for stateless deployments where data fits in a cookie
|
|
237
|
+
- `remix/session-storage/redis` — Redis-backed storage. Use for multi-process or multi-host deployments
|
|
351
238
|
- `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
|
-
|
|
354
|
-
|
|
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
|
|
239
|
+
- `remix/cookie` — `createCookie` for plain signed/unsigned cookies. Use for non-sensitive preferences where the client is allowed to control the value (theme, locale, dismissed banner). For state where tampering matters, prefer `remix/session`
|
|
240
|
+
- `remix/auth` — credentials, OAuth, OIDC, and Atmosphere providers. Use to define how identity is verified, start/finish external login, and refresh stored OAuth/OIDC token bundles with `refreshExternalAuth(...)`
|
|
241
|
+
- `remix/middleware/auth` — `auth({ schemes })`, `requireAuth`, the `Auth` context key. Use to resolve identity into the request context and to gate routes
|
|
360
242
|
|
|
361
243
|
### UI, Hydration, and Browser Behavior
|
|
362
244
|
|
|
363
|
-
- `remix/ui` — the component runtime: components, core mixins, `clientEntry`, `run`, `<Frame>`,
|
|
364
|
-
|
|
365
|
-
- `remix/ui/
|
|
366
|
-
|
|
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`
|
|
245
|
+
- `remix/ui` — the component runtime: components, core mixins, `clientEntry`, `run`, `<Frame>`, navigation helpers, and `createRoot`. Use for app UI behavior
|
|
246
|
+
- `remix/ui/server` — server rendering: `renderToStream`, `renderToString`. Use in the `app/actions/render.tsx` helper that returns HTML responses
|
|
247
|
+
- `remix/ui/animation` — animation APIs: `animateEntrance`, `animateExit`, `animateLayout`, `spring`, `tween`, and `easings`
|
|
248
|
+
- `remix/ui/<primitive>` — UI primitives, mixins, glyphs, and theme helpers. Current subpaths include `remix/ui/accordion`, `remix/ui/anchor`, `remix/ui/breadcrumbs`, `remix/ui/button`, `remix/ui/combobox`, `remix/ui/glyph`, `remix/ui/listbox`, `remix/ui/menu`, `remix/ui/popover`, `remix/ui/scroll-lock`, `remix/ui/select`, `remix/ui/separator`, and `remix/ui/theme`
|
|
374
249
|
- `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
|
-
|
|
377
|
-
- `remix/
|
|
378
|
-
|
|
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
|
|
250
|
+
- `remix/ui/jsx-runtime` and `remix/ui/jsx-dev-runtime` — JSX transform targets. Configured in `tsconfig.json`, rarely imported directly
|
|
251
|
+
- `remix/html-template` — escaped HTML template literals. Use when generating HTML outside the component system (RSS feeds, email bodies, error pages)
|
|
252
|
+
- `remix/file-storage` — backend-agnostic `File` storage interface. Use as the type bound for upload destinations
|
|
253
|
+
- `remix/file-storage/fs`, `remix/file-storage/memory`, `remix/file-storage/s3` — storage backends. Use to implement an upload destination
|
|
383
254
|
|
|
384
255
|
### Middleware
|
|
385
256
|
|
|
386
|
-
- `remix/middleware/static` — `staticFiles(dir)`. Use to serve files from `public/` exactly as
|
|
387
|
-
|
|
388
|
-
- `remix/
|
|
389
|
-
|
|
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']`
|
|
257
|
+
- `remix/middleware/static` — `staticFiles(dir)`. Use to serve files from `public/` exactly as they exist on disk
|
|
258
|
+
- `remix/middleware/form-data` — `formData()`. Use to parse `FormData` once and expose it via `get(FormData)` instead of calling `await request.formData()` in each action
|
|
259
|
+
- `remix/form-data-parser` — lower-level `parseFormData`, `FileUpload`. Use when implementing custom upload handlers. Upload handler errors propagate directly
|
|
260
|
+
- `remix/multipart-parser` and `remix/multipart-parser/node` — low-level multipart stream parsing. `MultipartPart.headers` is a plain object keyed by lower-case header name; read values with bracket notation such as `part.headers['content-type']`
|
|
395
261
|
- `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
|
-
|
|
398
|
-
- `remix/middleware/
|
|
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
|
|
262
|
+
- `remix/middleware/logger` — `logger()`. Use in development for request logs; pass `colors` to force terminal color output on or off
|
|
263
|
+
- `remix/middleware/method-override` — `methodOverride()`. Use when HTML forms need `PUT`, `PATCH`, or `DELETE`
|
|
264
|
+
- `remix/middleware/async-context` — `asyncContext()`, `getContext()`. Use when helpers outside actions need request context without threading it through every call
|
|
402
265
|
- `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
|
-
|
|
405
|
-
- `remix/middleware/cop` — cross-origin protection. Use to reject unsafe cross-origin browser
|
|
406
|
-
requests
|
|
266
|
+
- `remix/middleware/csrf` — `csrf(opts?)`. Use when session-backed forms mutate state and need synchronizer-token CSRF protection
|
|
267
|
+
- `remix/middleware/cop` — cross-origin protection. Use to reject unsafe cross-origin browser requests
|
|
407
268
|
|
|
408
269
|
### Test
|
|
409
270
|
|
|
410
271
|
- `remix/test` — `describe`, `it`, and lifecycle hooks. Use as the test framework
|
|
411
272
|
- `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
|
-
|
|
414
|
-
- `remix/
|
|
415
|
-
|
|
416
|
-
- `remix/
|
|
417
|
-
|
|
418
|
-
- `remix/
|
|
419
|
-
|
|
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
|
|
273
|
+
- `remix/node-fetch-server/test` — `createTestServer` for end-to-end tests that need a real local HTTP server around a Fetch handler
|
|
274
|
+
- `remix/cli` — programmatic Remix CLI API. Use the `remix` executable for project commands such as `remix test`, `remix routes`, `remix doctor`, and `remix version`
|
|
275
|
+
- `remix/assert` — assertion helpers. Use in place of `node:assert` so messages render cleanly in the runner
|
|
276
|
+
- `remix/terminal` — ANSI styles, color detection, style factories, and testable terminal streams. Use for CLIs and terminal output instead of hand-rolled escape sequences
|
|
277
|
+
- `remix/fs` — small filesystem helpers such as `openLazyFile` and `writeFile`. Use in Node-only app or tooling code when you need lazy file responses or safe file writes
|
|
278
|
+
- `remix/lazy-file` — `LazyFile` primitives and byte-range helpers. Use when implementing file or range responses below the higher-level response/file helpers
|
|
279
|
+
- `remix/mime` — content-type and MIME detection helpers. Use instead of maintaining app-local extension maps
|
|
280
|
+
- `remix/tar-parser` — streaming tar parsing. Use for import/export tooling that consumes tar archives
|
|
428
281
|
|
|
429
282
|
## Canonical Patterns
|
|
430
283
|
|
|
@@ -560,8 +413,7 @@ export default createController(routes.books, {
|
|
|
560
413
|
})
|
|
561
414
|
```
|
|
562
415
|
|
|
563
|
-
This shape works without JavaScript, returns a `Response` for every outcome, and is ready for
|
|
564
|
-
`clientEntry(...)` interactivity when the UI needs it.
|
|
416
|
+
This shape works without JavaScript, returns a `Response` for every outcome, and is ready for `clientEntry(...)` interactivity when the UI needs it.
|
|
565
417
|
|
|
566
418
|
### Build UI from handle props plus render
|
|
567
419
|
|
|
@@ -584,5 +436,4 @@ function Counter(handle: Handle<{ initialCount?: number; label: string }>) {
|
|
|
584
436
|
}
|
|
585
437
|
```
|
|
586
438
|
|
|
587
|
-
Only add `clientEntry(...)` and `run(...)` when the component needs browser interactivity or
|
|
588
|
-
browser-only APIs.
|
|
439
|
+
Only add `clientEntry(...)` and `run(...)` when the component needs browser interactivity or browser-only APIs.
|