create-rivet 0.1.0-alpha.1 → 0.1.0-alpha.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/README.md +8 -1
- package/deploy.d.ts +1 -1
- package/deploy.js +4 -4
- package/index.d.ts +6 -4
- package/index.js +13 -5
- package/main.d.ts +4 -1
- package/main.js +142 -26
- package/output.d.ts +24 -0
- package/output.js +66 -0
- package/package.json +3 -1
- package/prompt.d.ts +1 -0
- package/prompt.js +2 -2
- package/templates/starter-minimal/.agents/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-minimal/.agents/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-minimal/.agents/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-minimal/.claude/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-minimal/.claude/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-minimal/.claude/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-minimal/.cursor/rules/rivet-add-route.mdc +1 -1
- package/templates/starter-minimal/.cursor/rules/rivet-fix-resumability-error.mdc +7 -12
- package/templates/starter-minimal/.cursor/rules/rivet-port-react-component.mdc +3 -3
- package/templates/starter-minimal/.cursor/rules/rivet.mdc +6 -6
- package/templates/starter-minimal/.github/copilot-instructions.md +6 -6
- package/templates/starter-minimal/AGENTS.md +56 -78
- package/templates/starter-minimal/CLAUDE.md +56 -78
- package/templates/starter-minimal/README.md +3 -3
- package/templates/starter-minimal/package.json +2 -1
- package/templates/starter-minimal/src/document.ts +7 -6
- package/templates/starter-server/.agents/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-server/.agents/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-server/.agents/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-server/.agents/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-server/.agents/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-server/.claude/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-server/.claude/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-server/.claude/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-server/.claude/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-server/.claude/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-server/.cursor/rules/rivet-add-route.mdc +1 -1
- package/templates/starter-server/.cursor/rules/rivet-fix-resumability-error.mdc +7 -12
- package/templates/starter-server/.cursor/rules/rivet-port-react-component.mdc +3 -3
- package/templates/starter-server/.cursor/rules/rivet.mdc +6 -6
- package/templates/starter-server/.github/copilot-instructions.md +6 -6
- package/templates/starter-server/AGENTS.md +56 -78
- package/templates/starter-server/CLAUDE.md +56 -78
- package/templates/starter-server/README.md +5 -4
- package/templates/starter-server/package.json +2 -1
- package/templates/starter-server/serve.js +2 -0
- package/templates/starter-server/src/document.ts +7 -6
- package/templates/starter-static/.agents/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-static/.agents/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-static/.agents/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-static/.agents/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-static/.agents/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-static/.claude/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-static/.claude/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-static/.claude/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-static/.claude/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-static/.claude/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-static/.cursor/rules/rivet-add-route.mdc +1 -1
- package/templates/starter-static/.cursor/rules/rivet-fix-resumability-error.mdc +7 -12
- package/templates/starter-static/.cursor/rules/rivet-port-react-component.mdc +3 -3
- package/templates/starter-static/.cursor/rules/rivet.mdc +6 -6
- package/templates/starter-static/.github/copilot-instructions.md +6 -6
- package/templates/starter-static/AGENTS.md +56 -78
- package/templates/starter-static/CLAUDE.md +56 -78
- package/templates/starter-static/README.md +6 -5
- package/templates/starter-static/package.json +2 -1
- package/templates/starter-static/src/components/Counter.tsx +2 -6
- package/templates/starter-static/src/components/ReadingPlan.tsx +31 -0
- package/templates/starter-static/src/document.ts +7 -6
- package/templates/starter-static/src/routes/about.tsx +14 -1
- package/templates/starter-static/src/routes/index.tsx +2 -2
- package/templates/starter-ui/.agents/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-ui/.agents/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-ui/.agents/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-ui/.agents/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-ui/.agents/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-ui/.claude/skills/rivet-add-route/SKILL.md +1 -1
- package/templates/starter-ui/.claude/skills/rivet-fix-resumability-error/SKILL.md +7 -12
- package/templates/starter-ui/.claude/skills/rivet-port-react-component/SKILL.md +2 -2
- package/templates/starter-ui/.claude/skills/rivet-ui-design/references/component-composition.md +3 -3
- package/templates/starter-ui/.claude/skills/rivet-ui-design/references/rivet-implementation.md +1 -1
- package/templates/starter-ui/.cursor/rules/rivet-add-route.mdc +1 -1
- package/templates/starter-ui/.cursor/rules/rivet-fix-resumability-error.mdc +7 -12
- package/templates/starter-ui/.cursor/rules/rivet-port-react-component.mdc +3 -3
- package/templates/starter-ui/.cursor/rules/rivet.mdc +6 -6
- package/templates/starter-ui/.github/copilot-instructions.md +6 -6
- package/templates/starter-ui/AGENTS.md +56 -78
- package/templates/starter-ui/CLAUDE.md +56 -78
- package/templates/starter-ui/README.md +5 -4
- package/templates/starter-ui/package.json +2 -1
- package/templates/starter-ui/src/components/LikeButton.tsx +1 -1
- package/templates/starter-ui/src/components/ReadingPreferences.tsx +18 -0
- package/templates/starter-ui/src/document.ts +7 -6
- package/templates/starter-ui/src/routes/index.tsx +3 -1
- package/templates/starter-ui/vite.config.ts +3 -1
package/templates/starter-minimal/.agents/skills/rivet-ui-design/references/rivet-implementation.md
CHANGED
|
@@ -57,7 +57,7 @@ const failures = ["light", "dark"].flatMap((mode) => auditContrast(theme, mode a
|
|
|
57
57
|
- Preserve live props: read `props.name`, and use `splitProps`/`mergeProps` from `@rivetjs/runtime/props` for forwarding. Destructuring live props can read them once.
|
|
58
58
|
- Dynamic collections use `For` from `@rivetjs/runtime/dom` with a stable `by` key; its item and index arguments are accessors. Component render-prop state values can also be accessors; inspect the example/type before reading them.
|
|
59
59
|
- For renderable props use `Content` from `@rivetjs/runtime/jsx-runtime`. For deferred closed overlays/providers on resumable pages use render-function children `{() => …}` instead of already-created JSX. `AppLayout` named slots also take render functions, not JSX values.
|
|
60
|
-
-
|
|
60
|
+
- Handlers are plain functions; keep render output the same on server and browser (no `Date.now()` / random values in render). Browser reads (`window`, `document`, storage) belong in handlers or `clientEffect`, not server render. Import browser runtime helpers from `.tsx` modules for resume rewriting.
|
|
61
61
|
- Icons: `import { Icon } from "@rivetjs/kit/icon"` and icon data from `lucide`, then `<Icon icon={Search} />`. Keep decorative icons hidden and icon-only controls named. Do not invent SVG paths or use text glyphs as icons.
|
|
62
62
|
- CDK is headless: documented `rv-*` classes and `data-*` state support custom CSS, while behavior-critical styles remain inline. Use its overlay primitives, not a hand-built/native-dialog replacement with a different focus/dismissal model.
|
|
63
63
|
|
|
@@ -32,5 +32,5 @@ export default function Post({ data }: PageProps<"blog/[slug]">) {
|
|
|
32
32
|
- Static mode (`rivetRouter({ mode: "static" })`): `load(…)` runs at build time; dynamic `[param]` pages list their params in `entries(…)`.
|
|
33
33
|
- Server-only helpers go in `*.server.ts` files (browser code cannot import them: RV3008).
|
|
34
34
|
|
|
35
|
-
3. Link to it: plain `<a href="/blog/hello">` (client navigation), or typed `<Link to="/blog/hello">` from `@rivetjs/router/link`; programmatic `navigate()` from `@rivetjs/router/client`.
|
|
35
|
+
3. Link to it: plain `<a href="/blog/hello">` (client navigation when the page has the client router; document navigation otherwise), or typed `<Link to="/blog/hello">` from `@rivetjs/router/link`; programmatic `navigate()` from `@rivetjs/router/client`.
|
|
36
36
|
4. Run `npm run check` (`rivet sync` regenerates the typed routes in `.rivet/`, `rivet-check` reports `RV` codes; RV3101 = a link matches no route). Fix every diagnostic (`explain_diagnostic(code)`), then `npm run build`.
|
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rivet-fix-resumability-error
|
|
3
|
-
description:
|
|
3
|
+
description: "Diagnose and fix Rivet resumability errors - RV2xxx compiler diagnostics, RV1016 handler errors, RV2107 route data warnings, browser globals during server rendering, a component that renders in the browser instead of resuming. Use when a Rivet build, check or page fails with such an error."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/fix-resumability-error.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
7
7
|
|
|
8
8
|
# Fix a resumability error
|
|
9
9
|
|
|
10
|
-
A Rivet page is rendered on the server and resumed in the browser:
|
|
10
|
+
A Rivet page is rendered on the server and resumed in the browser: the first time a component is used, the browser runs its code once over the server HTML, starting from the signal values the server wrote. Errors come from code that renders differently on the two sides or from state/data that cannot be written into the page. Keep signal/store values as plain JSON data; the richer resume-format encoding (including registered classes) applies to route data.
|
|
11
11
|
|
|
12
12
|
1. Get the exact diagnostic: `npm run check` (`file:line:col - error RVnnnn`), the Vite overlay (`[RVnnnn] file:line:col`), or `check_code(source, filename)`. For the fix: `explain_diagnostic("RVnnnn")`; `check_code` also returns the quick-fixed source when one applies.
|
|
13
13
|
2. Common codes:
|
|
14
14
|
- RV2001 browser global (`window`, `document`, `localStorage`) read during server rendering → move it into a handler, `clientEffect(() => …)`, `clientEvent(window, "resize", …)` or `clientOnly(() => import("…"))`.
|
|
15
|
-
- RV2003 captured class instance (`new Intl.NumberFormat`, an SDK client) → capture plain data; create the object inside the handler or `clientEffect`, or build it with `reconstruct(() => new X(…))` from `@rivetjs/runtime/dom` (the browser runs the factory on first read). A data class declared in the component module (plain fields) resumes as is.
|
|
16
|
-
- RV2102 captured instance of a module class that cannot resume as data (`#private` field, a decorator, a field arrow using `super`, a base that is not a data class of the module, `window` / `document` / `WebSocket` read while constructing) → create it inside the handler, build it with `reconstruct(() => new X(…))`, keep only plain fields, or add `[serialize]()` + `static [revive](data)` (from `@rivetjs/runtime/resume-format`; package classes: `registerClass(Cls, { id, serialize, revive })` in a module imported by the server code and the resume entry). At render, `RV2102: X has [serialize] but no static [revive]` → add the `static [revive]`.
|
|
17
15
|
- RV1016 handler expression reads state when it binds (`onClick={a() ? f : g}`) → wrap: `onClick={(e) => (a() ? f(e) : g(e))}`.
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- RV2101 (warning, type-aware) a captured value's type does not serialize → as RV2003.
|
|
23
|
-
3. Build / prerender error `Cannot resume <file>#<Component>$<hash>: function is not serializable` (dev server: ``capture `<name>` function is not serializable``; dev build: `capture #<i> …`): a captured function Rivet did not compile (created at runtime by a library such as `memoize(fn)`, a `.bind(…)` written inside an object literal (keep `const inc = add.bind(null, 2)` in a local constant and store `inc`; `.bind(ctx)` of a function using `this` stays RV2007), a method, or held by an object built outside a component: at module level such as `const table = { format: formatEuro }`, or by your own hook / helper such as `function useFormat() { return { format: formatEuro }; }`). Wrap it in an inline arrow, or build the object inside the component (module-level and imported functions used by name there resume by reference). A library object holding functions (a chart, editor, socket client from a package): build it with `reconstruct(() => createChart(…))` from `@rivetjs/runtime/dom` — no value in the page, the browser runs the factory on first read (module level: one server instance per process, never read request state in it). A class instance at render (`X instance is not serializable`): your class → `reconstruct(() => new X(…))` or `[serialize]()` + `static [revive](data)`; a package class → `registerClass(X, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`. `NODE_ENV=development vite build` names the value path. Rules: `get_doc("docs:/best-practices/serializable-captures/")`.
|
|
24
|
-
4. State resets on the first change after resume: a child destructures its props (`function C({ value })`) and is re-created → read `props.value`. Copies of `props.children` in a local are not tracked → render `{props.children}` directly.
|
|
16
|
+
- RV2103 render is not deterministic (`Date.now()`, `Math.random()`, `crypto.randomUUID()` read while rendering) → keep the value in keyed state (`signal(Date.now())`) or read it in a handler / `clientEffect`.
|
|
17
|
+
- RV2104 (warning) keyed state created in a loop → create one signal / store outside the loop and keep the per-item values in it.
|
|
18
|
+
3. RV2107 (runtime warning) route data not encodable: a loader / action / error value holds a function, symbol, promise or an instance of an unregistered class; the page renders on the server but cannot resume. Return plain data; for a class use `registerClass(X, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`, imported on server and browser.
|
|
19
|
+
4. Dev warning that a component's markup differs and it renders in the browser: its render reads something that differs between server and browser (a browser global, a time, a random value, data the browser loads differently) → keep it in a signal, or move it into a `clientEffect`.
|
|
25
20
|
5. `.ts` module importing `@rivetjs/runtime/dom` (bundles the DOM runtime into SSR) → move that code into a `.tsx` file.
|
|
26
|
-
6. Re-run `npm run check` and `npm run build
|
|
21
|
+
6. Re-run `npm run check` and `npm run build`; both must be clean.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rivet-port-react-component
|
|
3
|
-
description: "Port a React (or Solid / Next.js) component or page to Rivet idioms - signals instead of useState, clientEffect instead of useEffect, For with a key, live props, class,
|
|
3
|
+
description: "Port a React (or Solid / Next.js) component or page to Rivet idioms - signals instead of useState, clientEffect instead of useEffect, For with a key, live props, class, ordinary function handlers. Use when the user pastes React code or asks to convert / migrate a component to Rivet."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/port-react-component.md in the Rivet repository: edit the source there and run the script. In an app created from a starter this file is yours to edit. -->
|
|
@@ -30,7 +30,7 @@ Map each construct (full table: `get_doc("docs:/start/coming-from-other-framewor
|
|
|
30
30
|
Then fix what resuming requires:
|
|
31
31
|
|
|
32
32
|
1. Move browser reads (`window`, `document`, `localStorage`, `matchMedia`) out of the component body into handlers or `clientEffect` (RV2001).
|
|
33
|
-
2. Handlers: inline arrows
|
|
33
|
+
2. Handlers: plain functions (inline arrows, local consts, imported functions); nothing they use needs to serialize. Keep values that differ between server and browser (`Date.now()`, random ids) in a signal or a handler.
|
|
34
34
|
3. Render props and item renderers stay functions (`{(item) => …}`).
|
|
35
35
|
4. Third-party React UI → `@rivetjs/ui` / `@rivetjs/cdk` equivalents: `list_components`, `get_component(name)`; React Aria-style names carry over (`onPress`, `isOpen={signal}`).
|
|
36
36
|
5. `check_code` the result, fix every RV code (`explain_diagnostic`), then `npm run check`.
|
package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/component-composition.md
CHANGED
|
@@ -40,13 +40,13 @@ Avoid nested interactive targets: an entire clickable card with nested buttons,
|
|
|
40
40
|
|
|
41
41
|
**Collection page:** heading + main create action → labeled search/filter controls → result count and collection → pagination. Show bulk actions only when selection exists; `ActionBar` or `Toolbar` may fit. Align filters and table columns to the same content edges. Provide initial-empty and filtered-empty states separately.
|
|
42
42
|
|
|
43
|
-
**Settings page:** heading/description → grouped `Fieldset` sections → controls/help/errors → save feedback/actions. Group related rows within a section with consistent spacing or separators. Do not wrap every preference in a card. Choose either explicit save or immediate updates for a region and make that behavior visible.
|
|
43
|
+
**Settings page:** heading/description → grouped `Fieldset` sections → controls/help/errors → save feedback/actions. Group related rows within a section with consistent spacing or separators; `CellSwitch`, `CellSelect` and `CellSlider` are full-row settings cells. Do not wrap every preference in a card. Choose either explicit save or immediate updates for a region and make that behavior visible.
|
|
44
44
|
|
|
45
45
|
**Detail/edit page:** summary and status → related content sections → contextual actions. Use a drawer for short contextual edits, a page for long/deep edits, and inline editing for small well-bounded values. Destructive actions belong near the affected object with clear labels and proportionate confirmation.
|
|
46
46
|
|
|
47
|
-
**Dashboard:** lead with metrics that support a decision, then the trend or collection needed to act. `Kpi`, `TrendChip`, and the chart exports may fit. Every chart needs a readable explanation or data alternative; meaningful units, time range and empty states matter more than decorative chart variety.
|
|
47
|
+
**Dashboard:** lead with metrics that support a decision, then the trend or collection needed to act. `Kpi`, `KpiGroup`, `Widget`, `NumberValue`, `TrendChip`, and the chart exports may fit; `ItemCard` / `ItemCardGroup` for settings rows, `Timeline` for activity history. Every chart needs a readable explanation or data alternative; meaningful units, time range and empty states matter more than decorative chart variety.
|
|
48
48
|
|
|
49
|
-
**Command picker:**
|
|
49
|
+
**Command picker:** `Command` (`@rivetjs/ui/command`) is the modal palette: a search field filtering grouped commands with virtual focus, Enter to run, Escape to clear then close; put extra search words in each item's `textValue`. For an inline picker, compose a labeled search field with `Autocomplete` + `Menu`. A command menu and a value-selecting list box are different tasks. Add pending, no-match, error and clear-query behavior; do not write a manual arrow-key loop. For actions on a right click, use `ContextMenu`.
|
|
50
50
|
|
|
51
51
|
## Reuse at the pattern boundary
|
|
52
52
|
|
package/templates/starter-minimal/.claude/skills/rivet-ui-design/references/rivet-implementation.md
CHANGED
|
@@ -57,7 +57,7 @@ const failures = ["light", "dark"].flatMap((mode) => auditContrast(theme, mode a
|
|
|
57
57
|
- Preserve live props: read `props.name`, and use `splitProps`/`mergeProps` from `@rivetjs/runtime/props` for forwarding. Destructuring live props can read them once.
|
|
58
58
|
- Dynamic collections use `For` from `@rivetjs/runtime/dom` with a stable `by` key; its item and index arguments are accessors. Component render-prop state values can also be accessors; inspect the example/type before reading them.
|
|
59
59
|
- For renderable props use `Content` from `@rivetjs/runtime/jsx-runtime`. For deferred closed overlays/providers on resumable pages use render-function children `{() => …}` instead of already-created JSX. `AppLayout` named slots also take render functions, not JSX values.
|
|
60
|
-
-
|
|
60
|
+
- Handlers are plain functions; keep render output the same on server and browser (no `Date.now()` / random values in render). Browser reads (`window`, `document`, storage) belong in handlers or `clientEffect`, not server render. Import browser runtime helpers from `.tsx` modules for resume rewriting.
|
|
61
61
|
- Icons: `import { Icon } from "@rivetjs/kit/icon"` and icon data from `lucide`, then `<Icon icon={Search} />`. Keep decorative icons hidden and icon-only controls named. Do not invent SVG paths or use text glyphs as icons.
|
|
62
62
|
- CDK is headless: documented `rv-*` classes and `data-*` state support custom CSS, while behavior-critical styles remain inline. Use its overlay primitives, not a hand-built/native-dialog replacement with a different focus/dismissal model.
|
|
63
63
|
|
|
@@ -33,5 +33,5 @@ export default function Post({ data }: PageProps<"blog/[slug]">) {
|
|
|
33
33
|
- Static mode (`rivetRouter({ mode: "static" })`): `load(…)` runs at build time; dynamic `[param]` pages list their params in `entries(…)`.
|
|
34
34
|
- Server-only helpers go in `*.server.ts` files (browser code cannot import them: RV3008).
|
|
35
35
|
|
|
36
|
-
3. Link to it: plain `<a href="/blog/hello">` (client navigation), or typed `<Link to="/blog/hello">` from `@rivetjs/router/link`; programmatic `navigate()` from `@rivetjs/router/client`.
|
|
36
|
+
3. Link to it: plain `<a href="/blog/hello">` (client navigation when the page has the client router; document navigation otherwise), or typed `<Link to="/blog/hello">` from `@rivetjs/router/link`; programmatic `navigate()` from `@rivetjs/router/client`.
|
|
37
37
|
4. Run `npm run check` (`rivet sync` regenerates the typed routes in `.rivet/`, `rivet-check` reports `RV` codes; RV3101 = a link matches no route). Fix every diagnostic (`explain_diagnostic(code)`), then `npm run build`.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Diagnose and fix Rivet resumability errors - RV2xxx compiler diagnostics, RV1016 handler errors,
|
|
2
|
+
description: "Diagnose and fix Rivet resumability errors - RV2xxx compiler diagnostics, RV1016 handler errors, RV2107 route data warnings, browser globals during server rendering, a component that renders in the browser instead of resuming. Use when a Rivet build, check or page fails with such an error."
|
|
3
3
|
globs:
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
@@ -8,20 +8,15 @@ alwaysApply: false
|
|
|
8
8
|
|
|
9
9
|
# Fix a resumability error
|
|
10
10
|
|
|
11
|
-
A Rivet page is rendered on the server and resumed in the browser:
|
|
11
|
+
A Rivet page is rendered on the server and resumed in the browser: the first time a component is used, the browser runs its code once over the server HTML, starting from the signal values the server wrote. Errors come from code that renders differently on the two sides or from state/data that cannot be written into the page. Keep signal/store values as plain JSON data; the richer resume-format encoding (including registered classes) applies to route data.
|
|
12
12
|
|
|
13
13
|
1. Get the exact diagnostic: `npm run check` (`file:line:col - error RVnnnn`), the Vite overlay (`[RVnnnn] file:line:col`), or `check_code(source, filename)`. For the fix: `explain_diagnostic("RVnnnn")`; `check_code` also returns the quick-fixed source when one applies.
|
|
14
14
|
2. Common codes:
|
|
15
15
|
- RV2001 browser global (`window`, `document`, `localStorage`) read during server rendering → move it into a handler, `clientEffect(() => …)`, `clientEvent(window, "resize", …)` or `clientOnly(() => import("…"))`.
|
|
16
|
-
- RV2003 captured class instance (`new Intl.NumberFormat`, an SDK client) → capture plain data; create the object inside the handler or `clientEffect`, or build it with `reconstruct(() => new X(…))` from `@rivetjs/runtime/dom` (the browser runs the factory on first read). A data class declared in the component module (plain fields) resumes as is.
|
|
17
|
-
- RV2102 captured instance of a module class that cannot resume as data (`#private` field, a decorator, a field arrow using `super`, a base that is not a data class of the module, `window` / `document` / `WebSocket` read while constructing) → create it inside the handler, build it with `reconstruct(() => new X(…))`, keep only plain fields, or add `[serialize]()` + `static [revive](data)` (from `@rivetjs/runtime/resume-format`; package classes: `registerClass(Cls, { id, serialize, revive })` in a module imported by the server code and the resume entry). At render, `RV2102: X has [serialize] but no static [revive]` → add the `static [revive]`.
|
|
18
16
|
- RV1016 handler expression reads state when it binds (`onClick={a() ? f : g}`) → wrap: `onClick={(e) => (a() ? f(e) : g(e))}`.
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- RV2101 (warning, type-aware) a captured value's type does not serialize → as RV2003.
|
|
24
|
-
3. Build / prerender error `Cannot resume <file>#<Component>$<hash>: function is not serializable` (dev server: ``capture `<name>` function is not serializable``; dev build: `capture #<i> …`): a captured function Rivet did not compile (created at runtime by a library such as `memoize(fn)`, a `.bind(…)` written inside an object literal (keep `const inc = add.bind(null, 2)` in a local constant and store `inc`; `.bind(ctx)` of a function using `this` stays RV2007), a method, or held by an object built outside a component: at module level such as `const table = { format: formatEuro }`, or by your own hook / helper such as `function useFormat() { return { format: formatEuro }; }`). Wrap it in an inline arrow, or build the object inside the component (module-level and imported functions used by name there resume by reference). A library object holding functions (a chart, editor, socket client from a package): build it with `reconstruct(() => createChart(…))` from `@rivetjs/runtime/dom` — no value in the page, the browser runs the factory on first read (module level: one server instance per process, never read request state in it). A class instance at render (`X instance is not serializable`): your class → `reconstruct(() => new X(…))` or `[serialize]()` + `static [revive](data)`; a package class → `registerClass(X, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`. `NODE_ENV=development vite build` names the value path. Rules: `get_doc("docs:/best-practices/serializable-captures/")`.
|
|
25
|
-
4. State resets on the first change after resume: a child destructures its props (`function C({ value })`) and is re-created → read `props.value`. Copies of `props.children` in a local are not tracked → render `{props.children}` directly.
|
|
17
|
+
- RV2103 render is not deterministic (`Date.now()`, `Math.random()`, `crypto.randomUUID()` read while rendering) → keep the value in keyed state (`signal(Date.now())`) or read it in a handler / `clientEffect`.
|
|
18
|
+
- RV2104 (warning) keyed state created in a loop → create one signal / store outside the loop and keep the per-item values in it.
|
|
19
|
+
3. RV2107 (runtime warning) route data not encodable: a loader / action / error value holds a function, symbol, promise or an instance of an unregistered class; the page renders on the server but cannot resume. Return plain data; for a class use `registerClass(X, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`, imported on server and browser.
|
|
20
|
+
4. Dev warning that a component's markup differs and it renders in the browser: its render reads something that differs between server and browser (a browser global, a time, a random value, data the browser loads differently) → keep it in a signal, or move it into a `clientEffect`.
|
|
26
21
|
5. `.ts` module importing `@rivetjs/runtime/dom` (bundles the DOM runtime into SSR) → move that code into a `.tsx` file.
|
|
27
|
-
6. Re-run `npm run check` and `npm run build
|
|
22
|
+
6. Re-run `npm run check` and `npm run build`; both must be clean.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Port a React (or Solid / Next.js) component or page to Rivet idioms - signals instead of useState, clientEffect instead of useEffect, For with a key, live props, class,
|
|
2
|
+
description: "Port a React (or Solid / Next.js) component or page to Rivet idioms - signals instead of useState, clientEffect instead of useEffect, For with a key, live props, class, ordinary function handlers. Use when the user pastes React code or asks to convert / migrate a component to Rivet."
|
|
3
3
|
globs:
|
|
4
4
|
alwaysApply: false
|
|
5
5
|
---
|
|
@@ -24,14 +24,14 @@ Map each construct (full table: `get_doc("docs:/start/coming-from-other-framewor
|
|
|
24
24
|
| `style="…"` strings | `style={{ color: c() }}` or `style:color={c()}` |
|
|
25
25
|
| `function Badge({ count })` | `function Badge(props: { count: number })` + `props.count` (props are live; destructuring reads once) |
|
|
26
26
|
| `React.lazy`, `<Suspense>` | `lazyComponent`, `Async`, `Suspense` (`@rivetjs/runtime/dom`) |
|
|
27
|
-
| Next.js `getServerSideProps`, Remix `loader` / `action` | `export const loader = load(…)` / `actions(…)` in the route file
|
|
27
|
+
| Next.js `getServerSideProps`, Remix `loader` / `action` | `export const loader = load(…)` / `actions(…)` in the route file |
|
|
28
28
|
| `useRouter().push`, `useSearchParams` | `navigate()`, `useSearchParams()` from `@rivetjs/router/client` |
|
|
29
29
|
| icon components / inline SVG | `<Icon icon={Check} />` from `@rivetjs/kit/icon` + `import { Check } from "lucide"` |
|
|
30
30
|
|
|
31
31
|
Then fix what resuming requires:
|
|
32
32
|
|
|
33
33
|
1. Move browser reads (`window`, `document`, `localStorage`, `matchMedia`) out of the component body into handlers or `clientEffect` (RV2001).
|
|
34
|
-
2. Handlers: inline arrows
|
|
34
|
+
2. Handlers: plain functions (inline arrows, local consts, imported functions); nothing they use needs to serialize. Keep values that differ between server and browser (`Date.now()`, random ids) in a signal or a handler.
|
|
35
35
|
3. Render props and item renderers stay functions (`{(item) => …}`).
|
|
36
36
|
4. Third-party React UI → `@rivetjs/ui` / `@rivetjs/cdk` equivalents: `list_components`, `get_component(name)`; React Aria-style names carry over (`onPress`, `isOpen={signal}`).
|
|
37
37
|
5. `check_code` the result, fix every RV code (`explain_diagnostic`), then `npm run check`.
|
|
@@ -10,18 +10,18 @@ alwaysApply: true
|
|
|
10
10
|
|
|
11
11
|
This project uses Rivet, a compiled, resumable JSX framework. It is NOT React and NOT Solid. Full instructions: `AGENTS.md` at the project root (read it before editing). Docs: the Rivet MCP server (`npx -y @rivetjs/mcp`: `search_docs`, `get_doc`, `get_component`, `check_code`, `explain_diagnostic`) or each docs site's `/llms.txt`.
|
|
12
12
|
|
|
13
|
-
- Components run
|
|
14
|
-
- Component
|
|
13
|
+
- Components run on the server; the browser resumes the page and runs a component once, over the server HTML, when it is first used. No hooks, no re-renders, no dependency arrays.
|
|
14
|
+
- Component bodies render on the server and run again on resume: no `window` / `document` / `localStorage` there (RV2001). Browser code goes in handlers, `clientEffect` / `clientEvent` / `clientOnly` from `@rivetjs/runtime/dom`.
|
|
15
15
|
- Signals (`@rivetjs/reactivity`): `const n = signal(0)`; read `n()`; write `n.set(1)` / `n.update((v) => v + 1)`; `computed(() => n() * 2)`; `untrack(n)` (no `.peek`).
|
|
16
16
|
- JSX: `class` (not `className`), `for`, events `onClick` / `onInput` (PascalCase; not `onclick`, not React's `onChange` for text), `model:value={signal}` for two-way inputs, style objects (no style strings).
|
|
17
17
|
- Props are live: read `props.x` where it is shown; destructuring reads once. `mergeProps` / `splitProps` from `@rivetjs/runtime/props`.
|
|
18
18
|
- Lists: `<For each={items} by={(i) => i.id}>{(item, index) => … item() … index()}</For>`; `Show`, `Switch` / `Match`, `ref()` from `@rivetjs/runtime/dom` (named imports).
|
|
19
|
-
-
|
|
19
|
+
- Resume: render the same thing on both sides (no `Date.now()` / `Math.random()` in a render, RV2103: keep it in a signal); no `signal()` in a loop (RV2104); keep signal/store values as plain JSON data; route loader/action data must be resume-format encodable (RV2107 runtime warning; classes via `registerClass`). No capture rules: a component given functions / services / JSX props adopts with its parent.
|
|
20
20
|
- Render props and item renderers are functions (`{(item) => …}`, `({ close }) => …`).
|
|
21
21
|
- Import `@rivetjs/runtime/dom` only from `.tsx` files, never from `.ts`.
|
|
22
22
|
- Routes: `src/routes/**.tsx`; `_layout.tsx`, `[param]`, `_404.tsx`; page exports `load(…)`, `actions(…)`, `head`, `live`; page `data` is a value, layout `data()` / `params()` / `url()` are accessors; `export const live = true` makes page `data` an accessor updated in place (forms in drawers).
|
|
23
|
-
- Forms: `@rivetjs/forms` (`defineForm`, `createForm`, `onSubmit
|
|
24
|
-
- i18n: `t("key", { param })` from `@rivetjs/i18n`, compile-time;
|
|
23
|
+
- Forms: `@rivetjs/forms` (`defineForm`, `createForm`, `onSubmit`); `FieldError` from `@rivetjs/forms/components`; server `action` / `invalid` from `@rivetjs/forms/server`.
|
|
24
|
+
- i18n: `t("key", { param })` from `@rivetjs/i18n`, compile-time; prefer literal keys (fixed-prefix template keys also support plain text without params).
|
|
25
25
|
- Icons: `<Icon icon={Check} />` from `@rivetjs/kit/icon` with lucide (`import { Check } from "lucide"`). Never inline SVG paths, never Unicode glyphs as icons.
|
|
26
26
|
- UI: `@rivetjs/ui/<component>` (styled compound parts `Select.Trigger`, `onPress`, two-way props take a signal); headless `@rivetjs/cdk/<module>`.
|
|
27
|
-
- After every change: `npm run check` (`rivet sync && rivet-check`) and fix every `RVnnnn` diagnostic; never edit `.rivet/` or `Generated … Do not edit` files.
|
|
27
|
+
- After every change: `npm run check` (`rivet sync && rivet-check && tsgo`) and fix every `RVnnnn` diagnostic; never edit `.rivet/` or `Generated … Do not edit` files.
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
|
|
5
5
|
This project uses Rivet, a compiled, resumable JSX framework. It is NOT React and NOT Solid. Full instructions: `AGENTS.md` at the project root (read it before editing). Docs: the Rivet MCP server (`npx -y @rivetjs/mcp`: `search_docs`, `get_doc`, `get_component`, `check_code`, `explain_diagnostic`) or each docs site's `/llms.txt`.
|
|
6
6
|
|
|
7
|
-
- Components run
|
|
8
|
-
- Component
|
|
7
|
+
- Components run on the server; the browser resumes the page and runs a component once, over the server HTML, when it is first used. No hooks, no re-renders, no dependency arrays.
|
|
8
|
+
- Component bodies render on the server and run again on resume: no `window` / `document` / `localStorage` there (RV2001). Browser code goes in handlers, `clientEffect` / `clientEvent` / `clientOnly` from `@rivetjs/runtime/dom`.
|
|
9
9
|
- Signals (`@rivetjs/reactivity`): `const n = signal(0)`; read `n()`; write `n.set(1)` / `n.update((v) => v + 1)`; `computed(() => n() * 2)`; `untrack(n)` (no `.peek`).
|
|
10
10
|
- JSX: `class` (not `className`), `for`, events `onClick` / `onInput` (PascalCase; not `onclick`, not React's `onChange` for text), `model:value={signal}` for two-way inputs, style objects (no style strings).
|
|
11
11
|
- Props are live: read `props.x` where it is shown; destructuring reads once. `mergeProps` / `splitProps` from `@rivetjs/runtime/props`.
|
|
12
12
|
- Lists: `<For each={items} by={(i) => i.id}>{(item, index) => … item() … index()}</For>`; `Show`, `Switch` / `Match`, `ref()` from `@rivetjs/runtime/dom` (named imports).
|
|
13
|
-
-
|
|
13
|
+
- Resume: render the same thing on both sides (no `Date.now()` / `Math.random()` in a render, RV2103: keep it in a signal); no `signal()` in a loop (RV2104); keep signal/store values as plain JSON data; route loader/action data must be resume-format encodable (RV2107 runtime warning; classes via `registerClass`). No capture rules: a component given functions / services / JSX props adopts with its parent.
|
|
14
14
|
- Render props and item renderers are functions (`{(item) => …}`, `({ close }) => …`).
|
|
15
15
|
- Import `@rivetjs/runtime/dom` only from `.tsx` files, never from `.ts`.
|
|
16
16
|
- Routes: `src/routes/**.tsx`; `_layout.tsx`, `[param]`, `_404.tsx`; page exports `load(…)`, `actions(…)`, `head`, `live`; page `data` is a value, layout `data()` / `params()` / `url()` are accessors; `export const live = true` makes page `data` an accessor updated in place (forms in drawers).
|
|
17
|
-
- Forms: `@rivetjs/forms` (`defineForm`, `createForm`, `onSubmit
|
|
18
|
-
- i18n: `t("key", { param })` from `@rivetjs/i18n`, compile-time;
|
|
17
|
+
- Forms: `@rivetjs/forms` (`defineForm`, `createForm`, `onSubmit`); `FieldError` from `@rivetjs/forms/components`; server `action` / `invalid` from `@rivetjs/forms/server`.
|
|
18
|
+
- i18n: `t("key", { param })` from `@rivetjs/i18n`, compile-time; prefer literal keys (fixed-prefix template keys also support plain text without params).
|
|
19
19
|
- Icons: `<Icon icon={Check} />` from `@rivetjs/kit/icon` with lucide (`import { Check } from "lucide"`). Never inline SVG paths, never Unicode glyphs as icons.
|
|
20
20
|
- UI: `@rivetjs/ui/<component>` (styled compound parts `Select.Trigger`, `onPress`, two-way props take a signal); headless `@rivetjs/cdk/<module>`.
|
|
21
|
-
- After every change: `npm run check` (`rivet sync && rivet-check`) and fix every `RVnnnn` diagnostic; never edit `.rivet/` or `Generated … Do not edit` files.
|
|
21
|
+
- After every change: `npm run check` (`rivet sync && rivet-check && tsgo`) and fix every `RVnnnn` diagnostic; never edit `.rivet/` or `Generated … Do not edit` files.
|