@remix-run/cli 0.3.1 → 0.3.3
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/package.json +3 -3
- package/src/lib/bootstrap-project.ts +9 -6
- package/template/.agents/skills/remix/SKILL.md +116 -263
- 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 +13 -34
- package/template/.agents/skills/remix/references/component-model.md +11 -26
- 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 +32 -48
- package/template/.agents/skills/remix/references/mixins-styling-events.md +14 -32
- package/template/.agents/skills/remix/references/routing-and-controllers.md +30 -59
- package/template/.agents/skills/remix/references/testing-patterns.md +13 -32
- package/template/app/ui/document.tsx +22 -17
- package/template/app/ui/scaffold-home-page.tsx +56 -47
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What This Covers
|
|
4
4
|
|
|
5
|
-
How server-rendered UI becomes interactive in the browser, and how the page updates without a full
|
|
6
|
-
navigation. Read this when the task involves:
|
|
5
|
+
How server-rendered UI becomes interactive in the browser, and how the page updates without a full navigation. Read this when the task involves:
|
|
7
6
|
|
|
8
7
|
- Marking a component for client-side hydration with `clientEntry`
|
|
9
8
|
- Booting the client runtime with `run`
|
|
@@ -12,19 +11,13 @@ navigation. Read this when the task involves:
|
|
|
12
11
|
- Server rendering with `renderToStream` or `renderToString`
|
|
13
12
|
- Managing the document `<head>`
|
|
14
13
|
|
|
15
|
-
For component-local state and updates, see `component-model.md`. For host-element behavior and
|
|
16
|
-
events, see `mixins-styling-events.md`.
|
|
14
|
+
For component-local state and updates, see `component-model.md`. For host-element behavior and events, see `mixins-styling-events.md`.
|
|
17
15
|
|
|
18
16
|
## Server First, Then Hydrate
|
|
19
17
|
|
|
20
|
-
Make the server route correct before adding `clientEntry(...)`. A POST should already do the right
|
|
21
|
-
thing on its own — return HTML, a redirect, or an error response — and a GET should already render
|
|
22
|
-
the page the user expects. `clientEntry` exists to layer interactivity on top of UI that already
|
|
23
|
-
works without it.
|
|
18
|
+
Make the server route correct before adding `clientEntry(...)`. A POST should already do the right thing on its own — return HTML, a redirect, or an error response — and a GET should already render the page the user expects. `clientEntry` exists to layer interactivity on top of UI that already works without it.
|
|
24
19
|
|
|
25
|
-
When server state changes after a mutation, prefer reloading a `<Frame>` when the UI region already
|
|
26
|
-
maps cleanly to a server-rendered route. Frames re-fetch the same route, so the rendering logic
|
|
27
|
-
stays in one place and the client does not need a parallel "state" API.
|
|
20
|
+
When server state changes after a mutation, prefer reloading a `<Frame>` when the UI region already maps cleanly to a server-rendered route. Frames re-fetch the same route, so the rendering logic stays in one place and the client does not need a parallel "state" API.
|
|
28
21
|
|
|
29
22
|
```tsx
|
|
30
23
|
on('submit', async (event, signal) => {
|
|
@@ -39,15 +32,11 @@ on('submit', async (event, signal) => {
|
|
|
39
32
|
})
|
|
40
33
|
```
|
|
41
34
|
|
|
42
|
-
Use polling or a small JSON state endpoint when the data changes outside this page, or when a tiny
|
|
43
|
-
shared widget would be heavier to model as a frame. Pick the lightest sync mechanism that preserves
|
|
44
|
-
clear ownership of rendering logic.
|
|
35
|
+
Use polling or a small JSON state endpoint when the data changes outside this page, or when a tiny shared widget would be heavier to model as a frame. Pick the lightest sync mechanism that preserves clear ownership of rendering logic.
|
|
45
36
|
|
|
46
37
|
## Client Entries
|
|
47
38
|
|
|
48
|
-
Use `clientEntry` to mark a component for client-side hydration. In source-served apps, prefer the
|
|
49
|
-
source module's `import.meta.url` as the entry ID and let server rendering map it to the public
|
|
50
|
-
asset URL:
|
|
39
|
+
Use `clientEntry` to mark a component for client-side hydration. In source-served apps, prefer the source module's `import.meta.url` as the entry ID and let server rendering map it to the public asset URL:
|
|
51
40
|
|
|
52
41
|
```tsx
|
|
53
42
|
import { clientEntry, on, type Handle } from 'remix/ui'
|
|
@@ -76,9 +65,7 @@ export const Counter = clientEntry(
|
|
|
76
65
|
)
|
|
77
66
|
```
|
|
78
67
|
|
|
79
|
-
On the server, provide `resolveClientEntry` to `renderToStream(...)` so source file URLs become
|
|
80
|
-
browser-loadable asset URLs. Keep this resolution in the render helper so component modules do not
|
|
81
|
-
hard-code deployment-specific asset paths:
|
|
68
|
+
On the server, provide `resolveClientEntry` to `renderToStream(...)` so source file URLs become browser-loadable asset URLs. Keep this resolution in the render helper so component modules do not hard-code deployment-specific asset paths:
|
|
82
69
|
|
|
83
70
|
```tsx
|
|
84
71
|
let stream = renderToStream(<App />, {
|
|
@@ -96,21 +83,15 @@ let stream = renderToStream(<App />, {
|
|
|
96
83
|
})
|
|
97
84
|
```
|
|
98
85
|
|
|
99
|
-
If the module export name differs from the component function name, include `#ExportName` in the
|
|
100
|
-
entry ID or return the exact export name from `resolveClientEntry`. A render helper that only
|
|
101
|
-
supports source-owned entries can also fail fast when `entryId` is not a `file://` URL.
|
|
86
|
+
If the module export name differs from the component function name, include `#ExportName` in the entry ID or return the exact export name from `resolveClientEntry`. A render helper that only supports source-owned entries can also fail fast when `entryId` is not a `file://` URL.
|
|
102
87
|
|
|
103
|
-
On the server, `clientEntry` components render like any other component. The server wraps their
|
|
104
|
-
output in comment markers and serializes props into a `<script type="application/json">` tag.
|
|
88
|
+
On the server, `clientEntry` components render like any other component. The server wraps their output in comment markers and serializes props into a `<script type="application/json">` tag.
|
|
105
89
|
|
|
106
|
-
Client entry props must be serializable: strings, numbers, booleans, `null`, `undefined`, plain
|
|
107
|
-
objects/arrays of the above, JSX elements, and `<Frame>` elements. Functions and class instances
|
|
108
|
-
cannot be passed.
|
|
90
|
+
Client entry props must be serializable: strings, numbers, booleans, `null`, `undefined`, plain objects/arrays of the above, JSX elements, and `<Frame>` elements. Functions and class instances cannot be passed.
|
|
109
91
|
|
|
110
92
|
## Booting the Client
|
|
111
93
|
|
|
112
|
-
Use `run` to start the client runtime. It scans the document for client entry markers, loads
|
|
113
|
-
modules, and hydrates each one:
|
|
94
|
+
Use `run` to start the client runtime. It scans the document for client entry markers, loads modules, and hydrates each one:
|
|
114
95
|
|
|
115
96
|
```tsx
|
|
116
97
|
import { run } from 'remix/ui'
|
|
@@ -137,10 +118,8 @@ await app.ready()
|
|
|
137
118
|
|
|
138
119
|
### `run` options
|
|
139
120
|
|
|
140
|
-
- **`loadModule(moduleUrl, exportName)`** (required) — return the component function for each
|
|
141
|
-
|
|
142
|
-
- **`resolveFrame(src, signal, target)`** (optional) — called when a `<Frame>` loads or reloads
|
|
143
|
-
content. `target` is available when frame targeting matters.
|
|
121
|
+
- **`loadModule(moduleUrl, exportName)`** (required) — return the component function for each client entry. Typically uses dynamic `import()`.
|
|
122
|
+
- **`resolveFrame(src, signal, target)`** (optional) — called when a `<Frame>` loads or reloads content. `target` is available when frame targeting matters.
|
|
144
123
|
|
|
145
124
|
### `app` methods
|
|
146
125
|
|
|
@@ -152,8 +131,7 @@ await app.ready()
|
|
|
152
131
|
|
|
153
132
|
## Frames
|
|
154
133
|
|
|
155
|
-
A `<Frame>` renders server content into the page. Frames stream after the initial HTML, nest inside
|
|
156
|
-
other frames, contain client entries, and can be reloaded without full page navigation.
|
|
134
|
+
A `<Frame>` renders server content into the page. Frames stream after the initial HTML, nest inside other frames, contain client entries, and can be reloaded without full page navigation.
|
|
157
135
|
|
|
158
136
|
```tsx
|
|
159
137
|
import { Frame } from 'remix/ui'
|
|
@@ -177,10 +155,8 @@ function App() {
|
|
|
177
155
|
|
|
178
156
|
### Blocking vs non-blocking
|
|
179
157
|
|
|
180
|
-
- **Without `fallback`** (blocking) — the server waits for frame content before sending the initial
|
|
181
|
-
|
|
182
|
-
- **With `fallback`** (non-blocking) — the fallback renders immediately; real content streams in
|
|
183
|
-
later and replaces it
|
|
158
|
+
- **Without `fallback`** (blocking) — the server waits for frame content before sending the initial HTML chunk
|
|
159
|
+
- **With `fallback`** (non-blocking) — the fallback renders immediately; real content streams in later and replaces it
|
|
184
160
|
|
|
185
161
|
### Reloading frames
|
|
186
162
|
|
|
@@ -197,21 +173,17 @@ await handle.frames.get('cart-summary')?.reload()
|
|
|
197
173
|
handle.frames.top.reload()
|
|
198
174
|
```
|
|
199
175
|
|
|
200
|
-
When a frame reloads, matching DOM nodes are updated in place. Client entries receive updated props
|
|
201
|
-
while preserving their local component state.
|
|
176
|
+
When a frame reloads, matching DOM nodes are updated in place. Client entries receive updated props while preserving their local component state.
|
|
202
177
|
|
|
203
178
|
### Nested frames
|
|
204
179
|
|
|
205
|
-
Frames can nest. Each frame owns its own DOM region and hydrates client entries independently.
|
|
206
|
-
During SSR, `handle.frame.src` points at the frame being rendered, while
|
|
207
|
-
`handle.frames.top.src` stays fixed at the outer document URL.
|
|
180
|
+
Frames can nest. Each frame owns its own DOM region and hydrates client entries independently. During SSR, `handle.frame.src` points at the frame being rendered, while `handle.frames.top.src` stays fixed at the outer document URL.
|
|
208
181
|
|
|
209
182
|
## Server Rendering
|
|
210
183
|
|
|
211
184
|
### `renderToStream`
|
|
212
185
|
|
|
213
|
-
Renders a component tree to a `ReadableStream<Uint8Array>`. Sends initial HTML immediately and
|
|
214
|
-
streams frame content as it resolves:
|
|
186
|
+
Renders a component tree to a `ReadableStream<Uint8Array>`. Sends initial HTML immediately and streams frame content as it resolves:
|
|
215
187
|
|
|
216
188
|
```tsx
|
|
217
189
|
import { renderToStream } from 'remix/ui/server'
|
|
@@ -235,11 +207,8 @@ return new Response(stream, {
|
|
|
235
207
|
Options:
|
|
236
208
|
|
|
237
209
|
- **`frameSrc`** — seeds SSR frame state; populates `handle.frame.src` and `handle.frames.top.src`
|
|
238
|
-
- **`topFrameSrc`** — overrides the root frame URL for nested frame renders (carry forward from
|
|
239
|
-
|
|
240
|
-
- **`resolveFrame(src, target, context)`** — return HTML string, `ReadableStream<Uint8Array>`, or a
|
|
241
|
-
promise of either. `context.currentFrameSrc` is the containing frame URL; `context.topFrameSrc`
|
|
242
|
-
is the outer document URL
|
|
210
|
+
- **`topFrameSrc`** — overrides the root frame URL for nested frame renders (carry forward from `resolveFrame` context)
|
|
211
|
+
- **`resolveFrame(src, target, context)`** — return HTML string, `ReadableStream<Uint8Array>`, or a promise of either. `context.currentFrameSrc` is the containing frame URL; `context.topFrameSrc` is the outer document URL
|
|
243
212
|
- **`onError(error)`** — called on rendering errors
|
|
244
213
|
|
|
245
214
|
### `renderToString`
|
|
@@ -253,8 +222,7 @@ let html = await renderToString(<App />)
|
|
|
253
222
|
|
|
254
223
|
### CSS in SSR
|
|
255
224
|
|
|
256
|
-
Components using the `css` mixin have styles collected during rendering and emitted as a single
|
|
257
|
-
`<style>` tag in `<head>`. No client-side style injection needed.
|
|
225
|
+
Components using the `css` mixin have styles collected during rendering and emitted as a single `<style>` tag in `<head>`. No client-side style injection needed.
|
|
258
226
|
|
|
259
227
|
## Navigation
|
|
260
228
|
|
|
@@ -293,5 +261,4 @@ function App() {
|
|
|
293
261
|
}
|
|
294
262
|
```
|
|
295
263
|
|
|
296
|
-
Put `title`, `meta`, `link`, and `style` tags inside an explicit `<head>`. Bare head-like tags
|
|
297
|
-
rendered outside `<head>` stay where they are — they are not moved into the document head for you.
|
|
264
|
+
Put `title`, `meta`, `link`, and `style` tags inside an explicit `<head>`. Bare head-like tags rendered outside `<head>` stay where they are — they are not moved into the document head for you.
|
|
@@ -2,22 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## What This Covers
|
|
4
4
|
|
|
5
|
-
How to compose the request lifecycle and bridge the router to a runtime. Read this when the task
|
|
6
|
-
involves:
|
|
5
|
+
How to compose the request lifecycle and bridge the router to a runtime. Read this when the task involves:
|
|
7
6
|
|
|
8
|
-
- Choosing or ordering built-in middleware in the
|
|
7
|
+
- Choosing or ordering built-in middleware in the stack
|
|
9
8
|
- Writing custom middleware that sets typed context values
|
|
10
|
-
- Adding fast-exit handling (static files, CORS preflights) versus request-enriching layers
|
|
11
|
-
(sessions, auth, data loading)
|
|
9
|
+
- Adding fast-exit handling (static files, CORS preflights) versus request-enriching layers (sessions, auth, data loading)
|
|
12
10
|
- Choosing when to keep the generated Node server versus switching server adapters
|
|
13
11
|
|
|
14
|
-
For data and persistence specifics, see `data-and-validation.md`. For session and auth specifics,
|
|
15
|
-
see `auth-and-sessions.md`.
|
|
12
|
+
For data and persistence specifics, see `data-and-validation.md`. For session and auth specifics, see `auth-and-sessions.md`.
|
|
16
13
|
|
|
17
14
|
## Middleware Stack
|
|
18
15
|
|
|
19
|
-
Middleware runs in order for every request. Place fast-exit middleware (static files) early and
|
|
20
|
-
request-enriching middleware (session, auth) later.
|
|
16
|
+
Middleware runs in order for every request. Place fast-exit middleware (static files) early and request-enriching middleware (session, auth) later.
|
|
21
17
|
|
|
22
18
|
Recommended ordering:
|
|
23
19
|
|
|
@@ -54,7 +50,7 @@ let router = createRouter({ middleware })
|
|
|
54
50
|
| Middleware | Import | Use when | Notes |
|
|
55
51
|
| -------------------------- | ---------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
56
52
|
| `staticFiles(dir, opts?)` | `remix/middleware/static` | Serve files from `public/` or another directory exactly as they exist on disk | Fast exit; usually near the top |
|
|
57
|
-
| `compression()` | `remix/middleware/compression` | Compress text-like responses | Usually
|
|
53
|
+
| `compression()` | `remix/middleware/compression` | Compress text-like responses | Usually app-wide |
|
|
58
54
|
| `logger()` | `remix/middleware/logger` | Log requests and responses | Often development-only; `colors` can force color output on/off |
|
|
59
55
|
| `cors(opts?)` | `remix/middleware/cors` | Endpoints must serve cross-origin browsers or preflight `OPTIONS` requests | Usually early so preflights can short-circuit |
|
|
60
56
|
| `cop(opts?)` | `remix/middleware/cop` | Reject unsafe cross-origin browser requests without synchronizer tokens | Put before session or CSRF when used |
|
|
@@ -64,33 +60,26 @@ let router = createRouter({ middleware })
|
|
|
64
60
|
| `csrf(opts?)` | `remix/middleware/csrf` | Session-backed form workflows need synchronizer-token CSRF protection | Requires `session()` before it |
|
|
65
61
|
| `asyncContext()` | `remix/middleware/async-context` | Helpers outside handlers need request context via `getContext()` | Add before helpers rely on it |
|
|
66
62
|
| `auth({ schemes })` | `remix/middleware/auth` | Resolve auth state into `context.get(Auth)` | Run after `session()` for session-backed auth |
|
|
67
|
-
| `requireAuth()` | `remix/middleware/auth` | A controller or action must reject anonymous access | Usually controller
|
|
63
|
+
| `requireAuth()` | `remix/middleware/auth` | A controller or action must reject anonymous access | Usually controller middleware or action middleware |
|
|
68
64
|
|
|
69
65
|
### Static files vs browser modules
|
|
70
66
|
|
|
71
|
-
- Use `staticFiles()` for files that should be served directly from disk, such as images, fonts,
|
|
72
|
-
|
|
73
|
-
- Use `remix/assets` when browser modules should be compiled and served from source files with
|
|
74
|
-
import rewriting, preloads, or fingerprinted URLs
|
|
67
|
+
- Use `staticFiles()` for files that should be served directly from disk, such as images, fonts, or already-built assets in `public/`
|
|
68
|
+
- Use `remix/assets` when browser modules should be compiled and served from source files with import rewriting, preloads, or fingerprinted URLs
|
|
75
69
|
|
|
76
70
|
### Ordering notes
|
|
77
71
|
|
|
78
72
|
- Put fast exits early: `staticFiles()`, `cors()` preflight handling, and `cop()` when used
|
|
79
|
-
- Parse request bodies before middleware that depends on them, such as `methodOverride()` and form
|
|
80
|
-
field token extraction in `csrf()`
|
|
73
|
+
- Parse request bodies before middleware that depends on them, such as `methodOverride()` and form field token extraction in `csrf()`
|
|
81
74
|
- Run `session()` before `csrf()` and before session-backed `auth()`
|
|
82
75
|
- Add `asyncContext()` before helpers or shared code call `getContext()`
|
|
83
|
-
- Keep route protection like `requireAuth()`
|
|
84
|
-
private
|
|
76
|
+
- Keep route protection like `requireAuth()` as controller middleware or action middleware unless the entire app is private
|
|
85
77
|
|
|
86
78
|
### Common stacks
|
|
87
79
|
|
|
88
|
-
- **Session-backed HTML app** -> `compression()`, `staticFiles()`, optional `cop()`, `formData()`,
|
|
89
|
-
|
|
90
|
-
- **
|
|
91
|
-
`auth({ schemes })`
|
|
92
|
-
- **Upload flow** -> `compression()`, `staticFiles()`, `formData({ uploadHandler })`, then
|
|
93
|
-
sessions, auth, and data-loading middleware as needed
|
|
80
|
+
- **Session-backed HTML app** -> `compression()`, `staticFiles()`, optional `cop()`, `formData()`, `methodOverride()`, `session()`, optional `csrf()`, `asyncContext()`, `auth({ schemes })`
|
|
81
|
+
- **Cross-origin API** -> `compression()`, `cors()`, optional `asyncContext()`, optional `auth({ schemes })`
|
|
82
|
+
- **Upload flow** -> `compression()`, `staticFiles()`, `formData({ uploadHandler })`, then sessions, auth, and data-loading middleware as needed
|
|
94
83
|
|
|
95
84
|
### Middleware with options
|
|
96
85
|
|
|
@@ -115,14 +104,11 @@ formData({
|
|
|
115
104
|
})
|
|
116
105
|
```
|
|
117
106
|
|
|
118
|
-
Errors thrown or rejected by `uploadHandler` propagate directly. Catch domain-specific upload
|
|
119
|
-
errors at the route boundary when they should become user-facing `Response` objects.
|
|
107
|
+
Errors thrown or rejected by `uploadHandler` propagate directly. Catch domain-specific upload errors at the route boundary when they should become user-facing `Response` objects.
|
|
120
108
|
|
|
121
109
|
## Writing Custom Middleware
|
|
122
110
|
|
|
123
|
-
Middleware is a function that receives `(context, next)`. Return a `Response` to short-circuit, call
|
|
124
|
-
and return `next()` when you need the downstream response, or return nothing when you only set
|
|
125
|
-
context and want the router to continue automatically.
|
|
111
|
+
Middleware is a function that receives `(context, next)`. Return a `Response` to short-circuit, call and return `next()` when you need the downstream response, or return nothing when you only set context and want the router to continue automatically.
|
|
126
112
|
|
|
127
113
|
### Setting context values
|
|
128
114
|
|
|
@@ -158,9 +144,7 @@ export function requireAdmin(): Middleware {
|
|
|
158
144
|
|
|
159
145
|
### Async context for helpers
|
|
160
146
|
|
|
161
|
-
`asyncContext()` stores the request context in `AsyncLocalStorage` so helpers can reach it
|
|
162
|
-
without the context being threaded through every call. Wrap `getContext()` in app-specific
|
|
163
|
-
helpers:
|
|
147
|
+
`asyncContext()` stores the request context in `AsyncLocalStorage` so helpers can reach it without the context being threaded through every call. Wrap `getContext()` in app-specific helpers:
|
|
164
148
|
|
|
165
149
|
```typescript
|
|
166
150
|
// app/utils/context.ts
|
|
@@ -191,17 +175,17 @@ export function getCurrentUserSafely() {
|
|
|
191
175
|
}
|
|
192
176
|
```
|
|
193
177
|
|
|
194
|
-
## Middleware
|
|
178
|
+
## Middleware Types
|
|
195
179
|
|
|
196
|
-
Middleware
|
|
180
|
+
Middleware has three API-owned forms:
|
|
197
181
|
|
|
198
|
-
1. **Router
|
|
182
|
+
1. **Router middleware** — runs for every request:
|
|
199
183
|
|
|
200
184
|
```typescript
|
|
201
|
-
let router = createRouter({ middleware: [
|
|
185
|
+
let router = createRouter({ middleware: [logger(), session(cookie, storage)] })
|
|
202
186
|
```
|
|
203
187
|
|
|
204
|
-
2. **Controller
|
|
188
|
+
2. **Controller middleware** — runs for the direct actions in one controller:
|
|
205
189
|
|
|
206
190
|
```typescript
|
|
207
191
|
export default createController(routes.account, {
|
|
@@ -210,23 +194,23 @@ Middleware can be applied at three levels:
|
|
|
210
194
|
})
|
|
211
195
|
```
|
|
212
196
|
|
|
213
|
-
Controller middleware does not flow into other controllers. Add the middleware to each
|
|
214
|
-
|
|
197
|
+
Controller middleware does not flow into other controllers. Add the middleware to each controller that needs it.
|
|
198
|
+
|
|
199
|
+
3. **Action middleware** — runs for a single action:
|
|
215
200
|
|
|
216
|
-
3. **Action-level** — runs for a single route:
|
|
217
201
|
```typescript
|
|
218
202
|
router.get(routes.account.index, {
|
|
219
203
|
middleware: [requireAuth()],
|
|
220
|
-
handler
|
|
204
|
+
handler(context) {
|
|
205
|
+
return render(<AccountPage identity={context.auth.identity} />)
|
|
206
|
+
},
|
|
221
207
|
})
|
|
222
208
|
```
|
|
223
209
|
|
|
210
|
+
Prefer inline arrays for `middleware` options. Use `RouterContext<typeof router>` to derive an app context from a router that uses inline middleware. Use `createMiddleware()` only when a chain is stored in a variable and its exact tuple type needs to be preserved, such as when deriving `MiddlewareContext<typeof rootMiddleware>` without a router value, exporting a reusable chain, or returning a chain from a factory.
|
|
211
|
+
|
|
224
212
|
## Node Server Setup
|
|
225
213
|
|
|
226
|
-
New apps already include a `server.ts` that adapts the app router with
|
|
227
|
-
`remix/node-fetch-server`. Keep that generated server unless the task specifically needs to change
|
|
228
|
-
runtime behavior such as host/protocol handling, TLS, HTTP/2, WebSockets, deployment lifecycle, or
|
|
229
|
-
test-only server setup.
|
|
214
|
+
New apps already include a `server.ts` that adapts the app router with `remix/node-fetch-server`. Keep that generated server unless the task specifically needs to change runtime behavior such as host/protocol handling, TLS, HTTP/2, WebSockets, deployment lifecycle, or test-only server setup.
|
|
230
215
|
|
|
231
|
-
Use `remix/node-fetch-server` when you want to keep owning a standard Node `http`, `https`, or
|
|
232
|
-
`http2` server directly.
|
|
216
|
+
Use `remix/node-fetch-server` when you want to keep owning a standard Node `http`, `https`, or `http2` server directly.
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## What This Covers
|
|
4
4
|
|
|
5
|
-
How to attach behavior, styles, and DOM-aware setup to host elements with `mix`. Read this when the
|
|
6
|
-
task involves:
|
|
5
|
+
How to attach behavior, styles, and DOM-aware setup to host elements with `mix`. Read this when the task involves:
|
|
7
6
|
|
|
8
7
|
- DOM event handling with `on(...)`
|
|
9
8
|
- Static styling with `css(...)` and dynamic styling with `style`
|
|
@@ -12,18 +11,13 @@ task involves:
|
|
|
12
11
|
- Native click, pointer, and keyboard behavior with `on(...)`, plus attributes with `attrs(...)`
|
|
13
12
|
- Element-level animation mixins from `remix/ui/animation`
|
|
14
13
|
|
|
15
|
-
For richer animation work (springs, tweens, layout transitions), see `animate-elements.md`. For
|
|
16
|
-
authoring custom mixins, see `create-mixins.md`. For component lifecycle and updates, see
|
|
17
|
-
`component-model.md`.
|
|
14
|
+
For richer animation work (springs, tweens, layout transitions), see `animate-elements.md`. For authoring custom mixins, see `create-mixins.md`. For component lifecycle and updates, see `component-model.md`.
|
|
18
15
|
|
|
19
|
-
Compose behavior on host elements with `mix`. Pass a single mixin directly (`mix={on(...)}`), or
|
|
20
|
-
an array when composing multiple mixins (`mix={[css(...), on(...)]}`). Core mixins are imported
|
|
21
|
-
from `remix/ui`; animation mixins are imported from `remix/ui/animation`.
|
|
16
|
+
Compose behavior on host elements with `mix`. Pass a single mixin directly (`mix={on(...)}`), or an array when composing multiple mixins (`mix={[css(...), on(...)]}`). Core mixins are imported from `remix/ui`; animation mixins are imported from `remix/ui/animation`.
|
|
22
17
|
|
|
23
18
|
## `on(type, handler, capture?)`
|
|
24
19
|
|
|
25
|
-
Attaches a typed DOM event handler. The handler receives the event and an `AbortSignal` that aborts
|
|
26
|
-
when the handler is re-entered or the component is removed — this prevents race conditions:
|
|
20
|
+
Attaches a typed DOM event handler. The handler receives the event and an `AbortSignal` that aborts when the handler is re-entered or the component is removed — this prevents race conditions:
|
|
27
21
|
|
|
28
22
|
```tsx
|
|
29
23
|
<input
|
|
@@ -56,9 +50,7 @@ Multiple events on the same element:
|
|
|
56
50
|
|
|
57
51
|
## `css(styles)`
|
|
58
52
|
|
|
59
|
-
Applies generated class names for CSS object styles. Produces static CSS rules inserted into the
|
|
60
|
-
document. Supports pseudo-selectors, pseudo-elements, attribute selectors, descendant selectors, and
|
|
61
|
-
media queries using `&` to reference the current element:
|
|
53
|
+
Applies generated class names for CSS object styles. Produces static CSS rules inserted into the document. Supports pseudo-selectors, pseudo-elements, attribute selectors, descendant selectors, and media queries using `&` to reference the current element:
|
|
62
54
|
|
|
63
55
|
```tsx
|
|
64
56
|
<button
|
|
@@ -80,9 +72,7 @@ media queries using `&` to reference the current element:
|
|
|
80
72
|
|
|
81
73
|
### `css(...)` vs `style` prop
|
|
82
74
|
|
|
83
|
-
Use `css(...)` for static styles, selectors, and media queries. Use `style` for dynamic values that
|
|
84
|
-
change often. Prefer CSS nested selectors for parent-state-affects-children over managing hover/focus
|
|
85
|
-
state in JavaScript:
|
|
75
|
+
Use `css(...)` for static styles, selectors, and media queries. Use `style` for dynamic values that change often. Prefer CSS nested selectors for parent-state-affects-children over managing hover/focus state in JavaScript:
|
|
86
76
|
|
|
87
77
|
```tsx
|
|
88
78
|
<div
|
|
@@ -96,8 +86,7 @@ state in JavaScript:
|
|
|
96
86
|
|
|
97
87
|
## `ref(callback)`
|
|
98
88
|
|
|
99
|
-
Calls a callback when an element is inserted. The callback receives the DOM node and an
|
|
100
|
-
`AbortSignal` that aborts when the element is removed:
|
|
89
|
+
Calls a callback when an element is inserted. The callback receives the DOM node and an `AbortSignal` that aborts when the element is removed:
|
|
101
90
|
|
|
102
91
|
```tsx
|
|
103
92
|
<input mix={ref((node) => node.focus())} />
|
|
@@ -116,8 +105,7 @@ The `ref` callback runs once when the element is first rendered, not on every up
|
|
|
116
105
|
|
|
117
106
|
## `link(href, options?)`
|
|
118
107
|
|
|
119
|
-
Adds client-side navigation behavior to any element. Makes non-anchor elements behave like Remix
|
|
120
|
-
navigation links:
|
|
108
|
+
Adds client-side navigation behavior to any element. Makes non-anchor elements behave like Remix navigation links:
|
|
121
109
|
|
|
122
110
|
```tsx
|
|
123
111
|
<article mix={link('/courses/intro')}>
|
|
@@ -125,20 +113,17 @@ navigation links:
|
|
|
125
113
|
</article>
|
|
126
114
|
```
|
|
127
115
|
|
|
128
|
-
Options match `NavigationOptions`: `src`, `target`, `history` (`'push' | 'replace'`),
|
|
129
|
-
`resetScroll`.
|
|
116
|
+
Options match `NavigationOptions`: `src`, `target`, `history` (`'push' | 'replace'`), `resetScroll`.
|
|
130
117
|
|
|
131
118
|
## Native press and keyboard interactions
|
|
132
119
|
|
|
133
|
-
Use native DOM events directly with `on(...)`. For buttons and links, `click` already includes
|
|
134
|
-
keyboard activation when the element has the right semantics:
|
|
120
|
+
Use native DOM events directly with `on(...)`. For buttons and links, `click` already includes keyboard activation when the element has the right semantics:
|
|
135
121
|
|
|
136
122
|
```tsx
|
|
137
123
|
<button mix={on('click', () => doAction())}>Action</button>
|
|
138
124
|
```
|
|
139
125
|
|
|
140
|
-
For gesture-specific behavior, compose the pointer or keyboard events the interaction actually
|
|
141
|
-
needs:
|
|
126
|
+
For gesture-specific behavior, compose the pointer or keyboard events the interaction actually needs:
|
|
142
127
|
|
|
143
128
|
```tsx
|
|
144
129
|
<button
|
|
@@ -177,8 +162,7 @@ Animates an element when it is inserted into the DOM. Config specifies the **sta
|
|
|
177
162
|
|
|
178
163
|
### `animateExit(config)`
|
|
179
164
|
|
|
180
|
-
Animates an element when it is removed. Config specifies the **ending** style. The element is kept
|
|
181
|
-
in the DOM until the animation completes:
|
|
165
|
+
Animates an element when it is removed. Config specifies the **ending** style. The element is kept in the DOM until the animation completes:
|
|
182
166
|
|
|
183
167
|
```tsx
|
|
184
168
|
{
|
|
@@ -206,8 +190,6 @@ Animates layout changes (position/size) using FLIP-style transforms:
|
|
|
206
190
|
}
|
|
207
191
|
```
|
|
208
192
|
|
|
209
|
-
Options: `duration` (default 200ms), `easing` (default spring snappy), `size` (boolean, default
|
|
210
|
-
true — include scale projection for size changes).
|
|
193
|
+
Options: `duration` (default 200ms), `easing` (default spring snappy), `size` (boolean, default true — include scale projection for size changes).
|
|
211
194
|
|
|
212
|
-
Always key elements you expect to animate. Use `...spring(preset)` to spread `duration` and
|
|
213
|
-
`easing` into any animation config.
|
|
195
|
+
Always key elements you expect to animate. Use `...spring(preset)` to spread `duration` and `easing` into any animation config.
|