create-rivet 0.1.0-alpha.0 → 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/templates/starter-minimal/.agents/skills/rivet-add-route/SKILL.md +9 -9
  4. package/templates/starter-minimal/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
  5. package/templates/starter-minimal/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
  6. package/templates/starter-minimal/.claude/skills/rivet-add-route/SKILL.md +9 -9
  7. package/templates/starter-minimal/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
  8. package/templates/starter-minimal/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
  9. package/templates/starter-minimal/.cursor/rules/rivet-add-route.mdc +9 -9
  10. package/templates/starter-minimal/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
  11. package/templates/starter-minimal/.cursor/rules/rivet-port-react-component.mdc +1 -1
  12. package/templates/starter-minimal/.cursor/rules/rivet.mdc +1 -1
  13. package/templates/starter-minimal/.github/copilot-instructions.md +1 -1
  14. package/templates/starter-minimal/AGENTS.md +50 -46
  15. package/templates/starter-minimal/CLAUDE.md +50 -46
  16. package/templates/starter-minimal/README.md +1 -1
  17. package/templates/starter-server/.agents/skills/rivet-add-route/SKILL.md +9 -9
  18. package/templates/starter-server/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
  19. package/templates/starter-server/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
  20. package/templates/starter-server/.claude/skills/rivet-add-route/SKILL.md +9 -9
  21. package/templates/starter-server/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
  22. package/templates/starter-server/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
  23. package/templates/starter-server/.cursor/rules/rivet-add-route.mdc +9 -9
  24. package/templates/starter-server/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
  25. package/templates/starter-server/.cursor/rules/rivet-port-react-component.mdc +1 -1
  26. package/templates/starter-server/.cursor/rules/rivet.mdc +1 -1
  27. package/templates/starter-server/.github/copilot-instructions.md +1 -1
  28. package/templates/starter-server/AGENTS.md +50 -46
  29. package/templates/starter-server/CLAUDE.md +50 -46
  30. package/templates/starter-server/README.md +3 -3
  31. package/templates/starter-server/src/routes/index.tsx +5 -5
  32. package/templates/starter-server/vite.config.ts +1 -1
  33. package/templates/starter-static/.agents/skills/rivet-add-route/SKILL.md +9 -9
  34. package/templates/starter-static/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
  35. package/templates/starter-static/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
  36. package/templates/starter-static/.claude/skills/rivet-add-route/SKILL.md +9 -9
  37. package/templates/starter-static/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
  38. package/templates/starter-static/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
  39. package/templates/starter-static/.cursor/rules/rivet-add-route.mdc +9 -9
  40. package/templates/starter-static/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
  41. package/templates/starter-static/.cursor/rules/rivet-port-react-component.mdc +1 -1
  42. package/templates/starter-static/.cursor/rules/rivet.mdc +1 -1
  43. package/templates/starter-static/.github/copilot-instructions.md +1 -1
  44. package/templates/starter-static/AGENTS.md +50 -46
  45. package/templates/starter-static/CLAUDE.md +50 -46
  46. package/templates/starter-static/README.md +1 -1
  47. package/templates/starter-ui/.agents/skills/rivet-add-route/SKILL.md +9 -9
  48. package/templates/starter-ui/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
  49. package/templates/starter-ui/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
  50. package/templates/starter-ui/.claude/skills/rivet-add-route/SKILL.md +9 -9
  51. package/templates/starter-ui/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
  52. package/templates/starter-ui/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
  53. package/templates/starter-ui/.cursor/rules/rivet-add-route.mdc +9 -9
  54. package/templates/starter-ui/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
  55. package/templates/starter-ui/.cursor/rules/rivet-port-react-component.mdc +1 -1
  56. package/templates/starter-ui/.cursor/rules/rivet.mdc +1 -1
  57. package/templates/starter-ui/.github/copilot-instructions.md +1 -1
  58. package/templates/starter-ui/AGENTS.md +50 -46
  59. package/templates/starter-ui/CLAUDE.md +50 -46
  60. package/templates/starter-ui/README.md +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # create-rivet
2
2
 
3
- Create a [Rivet](https://github.com/rivetjs/rivet) app.
3
+ Create a [Rivet](https://github.com/N1XUS/rivetjs) app.
4
4
 
5
5
  ```sh
6
6
  npm create rivet@latest my-app
@@ -43,4 +43,4 @@ With npm, options go after `--`.
43
43
 
44
44
  ## Nx monorepos
45
45
 
46
- The same templates as Nx projects, with cached `build` / `test` / `check` targets: [`@rivetjs/nx`](https://github.com/rivetjs/rivet/tree/main/libs/nx) (`npx create-nx-workspace@latest my-org --preset=@rivetjs/nx`, or `npx nx add @rivetjs/nx` in an existing workspace).
46
+ The same templates as Nx projects, with cached `build` / `test` / `check` targets: [`@rivetjs/nx`](https://github.com/N1XUS/rivetjs/tree/main/libs/nx) (`npx create-nx-workspace@latest my-org --preset=@rivetjs/nx`, or `npx nx add @rivetjs/nx` in an existing workspace).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-rivet",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Create a Rivet app: npm create rivet@latest",
@@ -1,23 +1,23 @@
1
1
  ---
2
2
  name: rivet-add-route
3
- description: "Add a page or endpoint to a Rivet app with file routes, server data (serverLoad), actions, head and layouts. Use when the user asks for a new page, URL, route, endpoint or loader in a Rivet project."
3
+ description: "Add a page or endpoint to a Rivet app with file routes, server data (load(…)), actions, head and layouts. Use when the user asks for a new page, URL, route, endpoint or loader in a Rivet project."
4
4
  ---
5
5
 
6
6
  <!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/add-route.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
  # Add a route to a Rivet app
9
9
 
10
- 1. Pick the file under `src/routes/` (URL = path): `about.tsx` → `/about`, `blog/[slug].tsx` → `/blog/:slug`, `docs/[[lang]]/x.tsx` (optional param), `files/[...path].tsx` (rest), `(group)/p.tsx` → `/p` (shares the group's `_layout.tsx`), `api/time.ts` → endpoint (`export const onGet = () => Response.json(…)`). `_layout.tsx` wraps its folder; `_404.tsx` / `_error.tsx` handle misses / errors. Look up details with `get_doc("docs:/guides/routing/file-routes/")`.
11
- 2. Write the page. Server code lives in `server…` exports (stripped from the browser build):
10
+ 1. Pick the file under `src/routes/` (URL = path): `about.tsx` → `/about`, `blog/[slug].tsx` → `/blog/:slug`, `docs/[[lang]]/x.tsx` (optional param), `files/[...path].tsx` (rest), `(group)/p.tsx` → `/p` (shares the group's `_layout.tsx`), `api/time.ts` → endpoint (`export const GET = onGet(() => Response.json(…))`, `onGet` from `@rivetjs/router`). `_layout.tsx` wraps its folder; `_404.tsx` / `_error.tsx` handle misses / errors. Look up details with `get_doc("docs:/guides/routing/file-routes/")`.
11
+ 2. Write the page. Server code lives in exports wrapped in a `@rivetjs/router` call — `load(…)`, `actions(…)`, `guard(…)`, `onGet(…)` … (stripped from the browser build):
12
12
 
13
13
  ```tsx
14
- import { notFound, type PageProps, type RequestEvent } from "@rivetjs/router";
14
+ import { notFound, type PageProps, type RequestEvent, load } from "@rivetjs/router";
15
15
 
16
- export const serverLoad = async ({ params }: RequestEvent<"blog/[slug]">) => {
16
+ export const loader = load(async ({ params }: RequestEvent<"blog/[slug]">) => {
17
17
  const post = await findPost(params.slug); // server-only: database, files, secrets
18
18
  if (!post) throw notFound();
19
19
  return { post };
20
- };
20
+ });
21
21
 
22
22
  export const head = { title: "Post" };
23
23
 
@@ -27,9 +27,9 @@ export default function Post({ data }: PageProps<"blog/[slug]">) {
27
27
  ```
28
28
 
29
29
  - Page props `data`, `params`, `url` are plain values; in `_layout.tsx` (`LayoutProps`) they are accessors: `data()`, `params()`, `url()`, plus `children`.
30
- - Mutations: `export const serverActions = { name: async ({ request }) => … }` + `<form method="post" action="?/name">`; with validation use `@rivetjs/forms` (`action(def, fn)` from `@rivetjs/forms/server`).
31
- - Access checks: `serverGuard` (throw / return `redirect()`, `deny()`, `notFound()`).
32
- - Static mode (`rivetRouter({ mode: "static" })`): `serverLoad` runs at build time; dynamic `[param]` pages list their params in `serverEntries`.
30
+ - Mutations: `export const forms = actions({ name: async ({ request }) => … })` + `<form method="post" action="?/name">`; with validation use `@rivetjs/forms` (`action(def, fn)` from `@rivetjs/forms/server`).
31
+ - Access checks: `guard(…)` (throw / return `redirect()`, `deny()`, `notFound()`).
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
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`.
@@ -24,16 +24,17 @@ export const customerForm = defineForm({ name: "customer", schema: z.object({ na
24
24
  import type { LivePageProps } from "@rivetjs/router";
25
25
  import { actionResult } from "@rivetjs/router/client";
26
26
  import { action, invalid } from "@rivetjs/forms/server";
27
+ import { load, actions } from "@rivetjs/router";
27
28
 
28
29
  export const live = true; // data is an accessor, refreshed in place after a submit: signals (open drawer, input, focus) survive
29
- export const serverLoad = ({ url }: { url: URL }) => ({ customers: [...customers], creating: url.searchParams.has("new") });
30
- export const serverActions = {
30
+ export const loader = load(({ url }: { url: URL }) => ({ customers: [...customers], creating: url.searchParams.has("new") }));
31
+ export const forms = actions({
31
32
  customer: action(customerForm, (value) => {
32
33
  if (customers.some((c) => c.email === value.email)) return invalid({ email: "Already a customer" });
33
34
  customers.push({ id: customers.length + 1, ...value });
34
35
  }),
35
- };
36
- export default function Customers({ data }: LivePageProps<typeof serverLoad>) {
36
+ });
37
+ export default function Customers({ data }: LivePageProps<typeof loader>) {
37
38
  /* … */
38
39
  }
39
40
  ```
@@ -58,5 +59,5 @@ export default function Customers({ data }: LivePageProps<typeof serverLoad>) {
58
59
 
59
60
  `onSubmit` resolves true once the server accepted the form and the page data is fresh: close the drawer then. Errors stay in the open drawer. Imports: `createForm, defaultValue, describedBy, fieldId, formId, isInvalid, onSubmit, submitting` from `@rivetjs/forms`, `FieldError` from `@rivetjs/forms/components`.
60
61
 
61
- 5. A layout `serverLoad` re-runs too (counts in headers update).
62
+ 5. A layout `load(…)` re-runs too (counts in headers update).
62
63
  6. Check: `check_code` on the page, `npm run check`, then try it with JavaScript disabled (plain POST, 400 re-render with the drawer open).
@@ -23,7 +23,7 @@ Map each construct (full table: `get_doc("docs:/start/coming-from-other-framewor
23
23
  | `style="…"` strings | `style={{ color: c() }}` or `style:color={c()}` |
24
24
  | `function Badge({ count })` | `function Badge(props: { count: number })` + `props.count` (props are live; destructuring reads once) |
25
25
  | `React.lazy`, `<Suspense>` | `lazyComponent`, `Async`, `Suspense` (`@rivetjs/runtime/dom`) |
26
- | Next.js `getServerSideProps`, Remix `loader` / `action` | `export const serverLoad` / `serverActions` in the route file |
26
+ | Next.js `getServerSideProps`, Remix `loader` / `action` | `export const loader = load(…)` / `actions(…)` in the route file |
27
27
  | `useRouter().push`, `useSearchParams` | `navigate()`, `useSearchParams()` from `@rivetjs/router/client` |
28
28
  | icon components / inline SVG | `<Icon icon={Check} />` from `@rivetjs/kit/icon` + `import { Check } from "lucide"` |
29
29
 
@@ -1,23 +1,23 @@
1
1
  ---
2
2
  name: rivet-add-route
3
- description: "Add a page or endpoint to a Rivet app with file routes, server data (serverLoad), actions, head and layouts. Use when the user asks for a new page, URL, route, endpoint or loader in a Rivet project."
3
+ description: "Add a page or endpoint to a Rivet app with file routes, server data (load(…)), actions, head and layouts. Use when the user asks for a new page, URL, route, endpoint or loader in a Rivet project."
4
4
  ---
5
5
 
6
6
  <!-- Generated by libs/mcp/ai/sync.ts from libs/mcp/ai/skills/add-route.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
  # Add a route to a Rivet app
9
9
 
10
- 1. Pick the file under `src/routes/` (URL = path): `about.tsx` → `/about`, `blog/[slug].tsx` → `/blog/:slug`, `docs/[[lang]]/x.tsx` (optional param), `files/[...path].tsx` (rest), `(group)/p.tsx` → `/p` (shares the group's `_layout.tsx`), `api/time.ts` → endpoint (`export const onGet = () => Response.json(…)`). `_layout.tsx` wraps its folder; `_404.tsx` / `_error.tsx` handle misses / errors. Look up details with `get_doc("docs:/guides/routing/file-routes/")`.
11
- 2. Write the page. Server code lives in `server…` exports (stripped from the browser build):
10
+ 1. Pick the file under `src/routes/` (URL = path): `about.tsx` → `/about`, `blog/[slug].tsx` → `/blog/:slug`, `docs/[[lang]]/x.tsx` (optional param), `files/[...path].tsx` (rest), `(group)/p.tsx` → `/p` (shares the group's `_layout.tsx`), `api/time.ts` → endpoint (`export const GET = onGet(() => Response.json(…))`, `onGet` from `@rivetjs/router`). `_layout.tsx` wraps its folder; `_404.tsx` / `_error.tsx` handle misses / errors. Look up details with `get_doc("docs:/guides/routing/file-routes/")`.
11
+ 2. Write the page. Server code lives in exports wrapped in a `@rivetjs/router` call — `load(…)`, `actions(…)`, `guard(…)`, `onGet(…)` … (stripped from the browser build):
12
12
 
13
13
  ```tsx
14
- import { notFound, type PageProps, type RequestEvent } from "@rivetjs/router";
14
+ import { notFound, type PageProps, type RequestEvent, load } from "@rivetjs/router";
15
15
 
16
- export const serverLoad = async ({ params }: RequestEvent<"blog/[slug]">) => {
16
+ export const loader = load(async ({ params }: RequestEvent<"blog/[slug]">) => {
17
17
  const post = await findPost(params.slug); // server-only: database, files, secrets
18
18
  if (!post) throw notFound();
19
19
  return { post };
20
- };
20
+ });
21
21
 
22
22
  export const head = { title: "Post" };
23
23
 
@@ -27,9 +27,9 @@ export default function Post({ data }: PageProps<"blog/[slug]">) {
27
27
  ```
28
28
 
29
29
  - Page props `data`, `params`, `url` are plain values; in `_layout.tsx` (`LayoutProps`) they are accessors: `data()`, `params()`, `url()`, plus `children`.
30
- - Mutations: `export const serverActions = { name: async ({ request }) => … }` + `<form method="post" action="?/name">`; with validation use `@rivetjs/forms` (`action(def, fn)` from `@rivetjs/forms/server`).
31
- - Access checks: `serverGuard` (throw / return `redirect()`, `deny()`, `notFound()`).
32
- - Static mode (`rivetRouter({ mode: "static" })`): `serverLoad` runs at build time; dynamic `[param]` pages list their params in `serverEntries`.
30
+ - Mutations: `export const forms = actions({ name: async ({ request }) => … })` + `<form method="post" action="?/name">`; with validation use `@rivetjs/forms` (`action(def, fn)` from `@rivetjs/forms/server`).
31
+ - Access checks: `guard(…)` (throw / return `redirect()`, `deny()`, `notFound()`).
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
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`.
@@ -24,16 +24,17 @@ export const customerForm = defineForm({ name: "customer", schema: z.object({ na
24
24
  import type { LivePageProps } from "@rivetjs/router";
25
25
  import { actionResult } from "@rivetjs/router/client";
26
26
  import { action, invalid } from "@rivetjs/forms/server";
27
+ import { load, actions } from "@rivetjs/router";
27
28
 
28
29
  export const live = true; // data is an accessor, refreshed in place after a submit: signals (open drawer, input, focus) survive
29
- export const serverLoad = ({ url }: { url: URL }) => ({ customers: [...customers], creating: url.searchParams.has("new") });
30
- export const serverActions = {
30
+ export const loader = load(({ url }: { url: URL }) => ({ customers: [...customers], creating: url.searchParams.has("new") }));
31
+ export const forms = actions({
31
32
  customer: action(customerForm, (value) => {
32
33
  if (customers.some((c) => c.email === value.email)) return invalid({ email: "Already a customer" });
33
34
  customers.push({ id: customers.length + 1, ...value });
34
35
  }),
35
- };
36
- export default function Customers({ data }: LivePageProps<typeof serverLoad>) {
36
+ });
37
+ export default function Customers({ data }: LivePageProps<typeof loader>) {
37
38
  /* … */
38
39
  }
39
40
  ```
@@ -58,5 +59,5 @@ export default function Customers({ data }: LivePageProps<typeof serverLoad>) {
58
59
 
59
60
  `onSubmit` resolves true once the server accepted the form and the page data is fresh: close the drawer then. Errors stay in the open drawer. Imports: `createForm, defaultValue, describedBy, fieldId, formId, isInvalid, onSubmit, submitting` from `@rivetjs/forms`, `FieldError` from `@rivetjs/forms/components`.
60
61
 
61
- 5. A layout `serverLoad` re-runs too (counts in headers update).
62
+ 5. A layout `load(…)` re-runs too (counts in headers update).
62
63
  6. Check: `check_code` on the page, `npm run check`, then try it with JavaScript disabled (plain POST, 400 re-render with the drawer open).
@@ -23,7 +23,7 @@ Map each construct (full table: `get_doc("docs:/start/coming-from-other-framewor
23
23
  | `style="…"` strings | `style={{ color: c() }}` or `style:color={c()}` |
24
24
  | `function Badge({ count })` | `function Badge(props: { count: number })` + `props.count` (props are live; destructuring reads once) |
25
25
  | `React.lazy`, `<Suspense>` | `lazyComponent`, `Async`, `Suspense` (`@rivetjs/runtime/dom`) |
26
- | Next.js `getServerSideProps`, Remix `loader` / `action` | `export const serverLoad` / `serverActions` in the route file |
26
+ | Next.js `getServerSideProps`, Remix `loader` / `action` | `export const loader = load(…)` / `actions(…)` in the route file |
27
27
  | `useRouter().push`, `useSearchParams` | `navigate()`, `useSearchParams()` from `@rivetjs/router/client` |
28
28
  | icon components / inline SVG | `<Icon icon={Check} />` from `@rivetjs/kit/icon` + `import { Check } from "lucide"` |
29
29
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Add a page or endpoint to a Rivet app with file routes, server data (serverLoad), actions, head and layouts. Use when the user asks for a new page, URL, route, endpoint or loader in a Rivet project."
2
+ description: "Add a page or endpoint to a Rivet app with file routes, server data (load(…)), actions, head and layouts. Use when the user asks for a new page, URL, route, endpoint or loader in a Rivet project."
3
3
  globs:
4
4
  alwaysApply: false
5
5
  ---
@@ -8,17 +8,17 @@ alwaysApply: false
8
8
 
9
9
  # Add a route to a Rivet app
10
10
 
11
- 1. Pick the file under `src/routes/` (URL = path): `about.tsx` → `/about`, `blog/[slug].tsx` → `/blog/:slug`, `docs/[[lang]]/x.tsx` (optional param), `files/[...path].tsx` (rest), `(group)/p.tsx` → `/p` (shares the group's `_layout.tsx`), `api/time.ts` → endpoint (`export const onGet = () => Response.json(…)`). `_layout.tsx` wraps its folder; `_404.tsx` / `_error.tsx` handle misses / errors. Look up details with `get_doc("docs:/guides/routing/file-routes/")`.
12
- 2. Write the page. Server code lives in `server…` exports (stripped from the browser build):
11
+ 1. Pick the file under `src/routes/` (URL = path): `about.tsx` → `/about`, `blog/[slug].tsx` → `/blog/:slug`, `docs/[[lang]]/x.tsx` (optional param), `files/[...path].tsx` (rest), `(group)/p.tsx` → `/p` (shares the group's `_layout.tsx`), `api/time.ts` → endpoint (`export const GET = onGet(() => Response.json(…))`, `onGet` from `@rivetjs/router`). `_layout.tsx` wraps its folder; `_404.tsx` / `_error.tsx` handle misses / errors. Look up details with `get_doc("docs:/guides/routing/file-routes/")`.
12
+ 2. Write the page. Server code lives in exports wrapped in a `@rivetjs/router` call — `load(…)`, `actions(…)`, `guard(…)`, `onGet(…)` … (stripped from the browser build):
13
13
 
14
14
  ```tsx
15
- import { notFound, type PageProps, type RequestEvent } from "@rivetjs/router";
15
+ import { notFound, type PageProps, type RequestEvent, load } from "@rivetjs/router";
16
16
 
17
- export const serverLoad = async ({ params }: RequestEvent<"blog/[slug]">) => {
17
+ export const loader = load(async ({ params }: RequestEvent<"blog/[slug]">) => {
18
18
  const post = await findPost(params.slug); // server-only: database, files, secrets
19
19
  if (!post) throw notFound();
20
20
  return { post };
21
- };
21
+ });
22
22
 
23
23
  export const head = { title: "Post" };
24
24
 
@@ -28,9 +28,9 @@ export default function Post({ data }: PageProps<"blog/[slug]">) {
28
28
  ```
29
29
 
30
30
  - Page props `data`, `params`, `url` are plain values; in `_layout.tsx` (`LayoutProps`) they are accessors: `data()`, `params()`, `url()`, plus `children`.
31
- - Mutations: `export const serverActions = { name: async ({ request }) => … }` + `<form method="post" action="?/name">`; with validation use `@rivetjs/forms` (`action(def, fn)` from `@rivetjs/forms/server`).
32
- - Access checks: `serverGuard` (throw / return `redirect()`, `deny()`, `notFound()`).
33
- - Static mode (`rivetRouter({ mode: "static" })`): `serverLoad` runs at build time; dynamic `[param]` pages list their params in `serverEntries`.
31
+ - Mutations: `export const forms = actions({ name: async ({ request }) => … })` + `<form method="post" action="?/name">`; with validation use `@rivetjs/forms` (`action(def, fn)` from `@rivetjs/forms/server`).
32
+ - Access checks: `guard(…)` (throw / return `redirect()`, `deny()`, `notFound()`).
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
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`.
@@ -25,16 +25,17 @@ export const customerForm = defineForm({ name: "customer", schema: z.object({ na
25
25
  import type { LivePageProps } from "@rivetjs/router";
26
26
  import { actionResult } from "@rivetjs/router/client";
27
27
  import { action, invalid } from "@rivetjs/forms/server";
28
+ import { load, actions } from "@rivetjs/router";
28
29
 
29
30
  export const live = true; // data is an accessor, refreshed in place after a submit: signals (open drawer, input, focus) survive
30
- export const serverLoad = ({ url }: { url: URL }) => ({ customers: [...customers], creating: url.searchParams.has("new") });
31
- export const serverActions = {
31
+ export const loader = load(({ url }: { url: URL }) => ({ customers: [...customers], creating: url.searchParams.has("new") }));
32
+ export const forms = actions({
32
33
  customer: action(customerForm, (value) => {
33
34
  if (customers.some((c) => c.email === value.email)) return invalid({ email: "Already a customer" });
34
35
  customers.push({ id: customers.length + 1, ...value });
35
36
  }),
36
- };
37
- export default function Customers({ data }: LivePageProps<typeof serverLoad>) {
37
+ });
38
+ export default function Customers({ data }: LivePageProps<typeof loader>) {
38
39
  /* … */
39
40
  }
40
41
  ```
@@ -59,5 +60,5 @@ export default function Customers({ data }: LivePageProps<typeof serverLoad>) {
59
60
 
60
61
  `onSubmit` resolves true once the server accepted the form and the page data is fresh: close the drawer then. Errors stay in the open drawer. Imports: `createForm, defaultValue, describedBy, fieldId, formId, isInvalid, onSubmit, submitting` from `@rivetjs/forms`, `FieldError` from `@rivetjs/forms/components`.
61
62
 
62
- 5. A layout `serverLoad` re-runs too (counts in headers update).
63
+ 5. A layout `load(…)` re-runs too (counts in headers update).
63
64
  6. Check: `check_code` on the page, `npm run check`, then try it with JavaScript disabled (plain POST, 400 re-render with the drawer open).
@@ -24,7 +24,7 @@ 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 serverLoad` / `serverActions` 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
 
@@ -19,7 +19,7 @@ This project uses Rivet, a compiled, resumable JSX framework. It is NOT React an
19
19
  - Captures must serialize: handlers are inline arrows or local consts. Functions stored by name in objects / arrays and imported functions (npm exports too) resume by reference; functions in objects built at module level or returned by library code / hooks do not. Class instances: data classes of the module, classes with `[serialize]` / `static [revive]`, package classes registered with `registerClass`. Library objects (`new Intl.NumberFormat`, SDK clients, charts) → create them in the handler or `reconstruct(() => new Intl.NumberFormat())` (`import { reconstruct } from "@rivetjs/runtime/dom"`). No `.bind` inside an object literal (inline in a prop or a local `const` of it is fine).
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
- - Routes: `src/routes/**.tsx`; `_layout.tsx`, `[param]`, `_404.tsx`; page exports `serverLoad`, `serverActions`, `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).
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
23
  - Forms: `@rivetjs/forms` (`defineForm`, `createForm`, `onSubmit`, `FieldError`; server `action` / `invalid` from `@rivetjs/forms/server`).
24
24
  - i18n: `t("key", { param })` from `@rivetjs/i18n`, compile-time; key a string literal.
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.
@@ -13,7 +13,7 @@ This project uses Rivet, a compiled, resumable JSX framework. It is NOT React an
13
13
  - Captures must serialize: handlers are inline arrows or local consts. Functions stored by name in objects / arrays and imported functions (npm exports too) resume by reference; functions in objects built at module level or returned by library code / hooks do not. Class instances: data classes of the module, classes with `[serialize]` / `static [revive]`, package classes registered with `registerClass`. Library objects (`new Intl.NumberFormat`, SDK clients, charts) → create them in the handler or `reconstruct(() => new Intl.NumberFormat())` (`import { reconstruct } from "@rivetjs/runtime/dom"`). No `.bind` inside an object literal (inline in a prop or a local `const` of it is fine).
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
- - Routes: `src/routes/**.tsx`; `_layout.tsx`, `[param]`, `_404.tsx`; page exports `serverLoad`, `serverActions`, `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).
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
17
  - Forms: `@rivetjs/forms` (`defineForm`, `createForm`, `onSubmit`, `FieldError`; server `action` / `invalid` from `@rivetjs/forms/server`).
18
18
  - i18n: `t("key", { param })` from `@rivetjs/i18n`, compile-time; key a string literal.
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.
@@ -106,7 +106,7 @@ import { For, Show, Switch, Match } from "@rivetjs/runtime/dom";
106
106
 
107
107
  - `For`: `by` key is required; `item` and `index` are **accessors** (`todo()`, `index()`). Use for lists that change. `.map()` is fine for fixed data (loader data, constants).
108
108
  - Conditionals: `cond() && <p />`, ternary, or `Show`. Always read the signal (`cond()`, not `cond`).
109
- - Other helpers: `Async`, `Suspense`, `ErrorBoundary`, `Dynamic`, `Portal`, `Provider`, `lazyComponent`, `clientOnly`.
109
+ - Other helpers: `Async`, `resource` (async data as a signal: `const user = resource(() => id(), (id, info) => load(id, info.signal))`; keep it in one `const` in the component, not destructured; fetchers may be passed by name; read inside `Suspense` so the server waits), `Suspense`, `ErrorBoundary`, `Dynamic`, `Portal`, `Provider`, `lazyComponent`, `clientOnly`.
110
110
  - A function child `{() => …}` re-runs when the signals its body reads change.
111
111
 
112
112
  ### Refs
@@ -140,8 +140,8 @@ const theme = useContext(ThemeContext); // import { useContext } from "@rivetjs/
140
140
 
141
141
  Everything a handler, effect, computed or binding uses from its component (its **captures**) is written into the HTML. It must be serializable.
142
142
 
143
- - OK: primitives, plain objects/arrays, `Date`, `URL`, `URLSearchParams`, `Map`, `Set`, `RegExp`, typed arrays, `ArrayBuffer`, `DataView`, `Error`, signals, `computed`, and functions Rivet compiled: inline arrows, local `const` functions, inline callback props, functions in object literals, returned functions, inline functions written into a value a handler uses (`api.inc = () => …`, `signal(() => …)`, `[() => …]`), local functions stored by name into such a value (`api.open = open`, `list.push(open)`), module-level or imported functions used by name inside a component (called by a handler, passed as a prop, kept in a local const, put in an object / array / call argument).
144
- - Not OK: class instances (`new Intl.NumberFormat()`, `new Api()` → RV2003; a data class declared in the component module resumes — no `#private` field, no base other than a data class of that module, no decorator, no `window` / `document` read while constructing, else RV2102; `reconstruct(() => new Api(…))` from `@rivetjs/runtime/dom` builds it in the browser on first read (no value in the page); a resource holder resumes with `[serialize]()` + `static [revive](data)`, a package class with `registerClass(Cls, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`), `Promise`, `WeakMap`, `Symbol`, `Error`, `.bind(...)` written inside an object literal (RV2014; inline in a prop / handler or a local `const inc = add.bind(null, 2)` of a compiled function used by name is OK; `.bind(ctx)` of a `this` function stays RV2007), methods / getters, functions using `this`, functions created at runtime by libraries (`memoize(fn)`), module-level / imported functions put into objects only outside a component (module level, hooks, helpers).
143
+ - OK: primitives, plain objects/arrays, `Date`, `URL`, `URLSearchParams`, `Map`, `Set`, `RegExp`, typed arrays, `ArrayBuffer`, `DataView`, `Error`, `bigint`, signals, `computed`, and functions Rivet compiled: inline arrows, local `const` functions, inline callback props, functions in object literals, returned functions, inline functions written into a value a handler uses (`api.inc = () => …`, `signal(() => …)`, `[() => …]`), local functions stored by name into such a value (`api.open = open`, `list.push(open)`), module-level or imported functions used by name inside a component (called by a handler, passed as a prop, kept in a local const, put in an object / array / call argument).
144
+ - Not OK: class instances (`new Intl.NumberFormat()`, `new Api()` → RV2003; a data class declared in the component module resumes — no `#private` field, no base other than a data class of that module, no decorator, no `window` / `document` read while constructing, else RV2102; `reconstruct(() => new Api(…))` from `@rivetjs/runtime/dom` builds it in the browser on first read (no value in the page); a resource holder resumes with `[serialize]()` + `static [revive](data)`, a package class with `registerClass(Cls, { id, serialize, revive })` from `@rivetjs/runtime/resume-format`), `Promise`, `WeakMap`, `Symbol`, `.bind(...)` written inside an object literal (RV2014; inline in a prop / handler or a local `const inc = add.bind(null, 2)` of a compiled function used by name is OK; `.bind(ctx)` of a `this` function stays RV2007), methods / getters, functions using `this`, functions created at runtime by libraries (`memoize(fn)`), module-level / imported functions put into objects only outside a component (module level, hooks, helpers).
145
145
 
146
146
  ```tsx
147
147
  // Avoid: captures a class instance
@@ -175,32 +175,32 @@ src/
175
175
  docs/[[lang]]/x.tsx optional param
176
176
  files/[...path].tsx rest param
177
177
  (marketing)/p.tsx /p (group: no segment, shares a layout)
178
- api/time.ts endpoint (exports onGet/onPost…, returns Response)
178
+ api/time.ts endpoint (exports onGet(…)/onPost(…)…, returns Response)
179
179
  _404.tsx _error.tsx
180
180
  ```
181
181
 
182
- - Files/folders starting with `_` (except `_layout`, `_404`, `_error`), `.`, `*.test.*`, `*.d.ts` are ignored. `*.server.ts` = server-only module (cannot be imported by browser code, RV3008).
182
+ - Files/folders starting with `_` (except `_layout`, `_404`, `_error`), `.`, `*.test.*`, `*.d.ts` are ignored. `*.server.ts` = server-only module: browser code cannot use it (RV3008 in a route module's browser part); a page that imports one itself becomes a document route (full page load), a component, layout or error page that reaches one fails the build (RV3011).
183
183
  - `.rivet/` is generated (route types `routes.d.ts`, editor manifest): never edit; `rivet sync` regenerates it.
184
184
 
185
185
  ### Page module exports
186
186
 
187
187
  ```tsx
188
- import { redirect, notFound, type PageProps, type RequestEvent } from "@rivetjs/router";
188
+ import { redirect, notFound, type PageProps, type RequestEvent, load, actions } from "@rivetjs/router";
189
189
 
190
- export const serverLoad = async ({ params }: RequestEvent<"blog/[slug]">) => {
190
+ export const loader = load(async ({ params }: RequestEvent<"blog/[slug]">) => {
191
191
  const post = await findPost(params.slug);
192
192
  if (!post) throw notFound();
193
193
  return { post };
194
- };
194
+ });
195
195
 
196
- export const serverActions = {
196
+ export const forms = actions({
197
197
  // <form method="post" action="?/comment">
198
198
  comment: async ({ request }: RequestEvent<"blog/[slug]">) => {
199
199
  const text = String((await request.formData()).get("text") ?? "");
200
200
  await saveComment(text);
201
201
  return redirect("/blog", 303);
202
202
  },
203
- };
203
+ });
204
204
 
205
205
  export const head = { title: "Post" }; // or ({ data, params, url }) => Head
206
206
 
@@ -209,7 +209,7 @@ export default function Post({ data, params }: PageProps<"blog/[slug]">) {
209
209
  }
210
210
  ```
211
211
 
212
- - Server exports: `serverLoad`, `serverActions`, `serverGuard` (throw/return `redirect()`, `deny()`, `notFound()`, `error()`), `serverHeaders`, `serverEntries` (prerender params), `prerender`, `onRequest` / `onGet` / `onPost` …. The compiler strips them (and code only they use) from the browser build.
212
+ - Server exports: `load(…)`, `actions(…)`, `guard(…)` (throw/return `redirect()`, `deny()`, `notFound()`, `error()`), `headers(…)`, `entries(…)` (prerender params), `prerender`, `onRequest(…)` / `onGet(…)` / `onPost(…)` …. `load(fn, { defer: true })` = the page streams without waiting: `data` is a `Deferred` handle, render it only through `<Async load={() => data}>` (layouts: `data()`) or `resource(() => data)`, never `data.field`; it cannot set headers / cookies. The compiler strips them (and code only they use) from the browser build.
213
213
  - `event`: `request`, `url`, `params`, `locals`, `signal`, `cookies`, `setHeader`.
214
214
  - Page props: `{ data, params, url }` — plain values. Layout props (`LayoutProps`): `data()`, `params()`, `url()` are **accessors**, plus `children`.
215
215
 
@@ -217,7 +217,7 @@ export default function Post({ data, params }: PageProps<"blog/[slug]">) {
217
217
  // Avoid in a layout
218
218
  <header>{data.user}</header>;
219
219
  // Prefer
220
- export default function Layout({ data, children }: LayoutProps<typeof serverLoad>) {
220
+ export default function Layout({ data, children }: LayoutProps<typeof loader>) {
221
221
  return (
222
222
  <>
223
223
  <header>{data().user}</header>
@@ -229,7 +229,7 @@ export default function Layout({ data, children }: LayoutProps<typeof serverLoad
229
229
 
230
230
  ### Live pages
231
231
 
232
- - `export const live = true` + `LivePageProps<typeof serverLoad>`: `data` becomes an accessor (`data().customers`). Same-URL revalidation (a `@rivetjs/forms` submit, `invalidate()`, search-param change) updates `data` in place: the page is not re-rendered, signals (an open drawer, typed input, focus) survive. Use it for forms in drawers / dialogs.
232
+ - `export const live = true` + `LivePageProps<typeof loader>`: `data` becomes an accessor (`data().customers`). Same-URL revalidation (a `@rivetjs/forms` submit, `invalidate()`, search-param change) updates `data` in place: the page is not re-rendered, signals (an open drawer, typed input, focus) survive. Use it for forms in drawers / dialogs.
233
233
  - Don't copy `data()` into a signal (it never updates). Render lists with `<For each={data().items} by={…}>`.
234
234
 
235
235
  ### Links and navigation
@@ -242,7 +242,7 @@ export default function Layout({ data, children }: LayoutProps<typeof serverLoad
242
242
  ### Modes (`vite.config.ts`)
243
243
 
244
244
  - `rivetRouter()` = `mode: "server"` (render per request; `npm run build` → `dist/server`, `npm start`).
245
- - `rivetRouter({ mode: "static" })` = prerender to HTML files (`prerender.js` / `prerenderBuild`). `serverLoad` runs at build time. A page with nothing to resume (no handlers, signals in markup, client effects, forms) ships zero JS and its links load documents; `clientRouter: true` keeps in-place navigation on every page.
245
+ - `rivetRouter({ mode: "static" })` = prerender to HTML files (`prerender.js` / `prerenderBuild`). `load(…)` runs at build time. A page with nothing to resume (no handlers, signals in markup, client effects, forms) ships zero JS and its links load documents; `clientRouter: true` keeps in-place navigation on every page.
246
246
  - `mode: "csr"` = SPA, no server code.
247
247
 
248
248
  ### Forms (`@rivetjs/forms`)
@@ -260,12 +260,13 @@ import { action, invalid } from "@rivetjs/forms/server";
260
260
  import { actionResult } from "@rivetjs/router/client";
261
261
  import { createForm, fieldId, formId, onInput, onBlur, onSubmit, defaultValue, isInvalid, describedBy } from "@rivetjs/forms";
262
262
  import { FieldError, ErrorSummary } from "@rivetjs/forms/components";
263
+ import { actions } from "@rivetjs/router";
263
264
 
264
- export const serverActions = {
265
+ export const forms = actions({
265
266
  contact: action(contactForm, async (value) => {
266
267
  /* typed, valid */
267
268
  }),
268
- };
269
+ });
269
270
 
270
271
  export default function Contact() {
271
272
  const form = createForm(contactForm, { result: actionResult() });
@@ -403,36 +404,39 @@ import { Select } from "@rivetjs/ui/select";
403
404
  - Where: Vite (terminal + overlay) as `[RV1016] src/x.tsx:12:8: message`; `rivet-check` as `file:line:col - error RV1016: message`; editor via the TS plugin. The MCP tool `explain_diagnostic("RV1016")` gives the meaning, the mechanical fix and an Avoid / Prefer pair; `check_code` returns the fix and the fixed source for a whole file.
404
405
  - Ranges: 1xxx JSX / attributes, 2xxx resume, 3xxx imports / helpers / route modules, 4xxx i18n, 5xxx styles, 6xxx slots, 7xxx config, 9xxx internal.
405
406
 
406
- | Code | Meaning | Fix |
407
- | --------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
408
- | RV1000 | Parse error | fix syntax |
409
- | RV1005 | HTML nesting the parser would repair (`<div>` in `<p>`) | valid nesting |
410
- | RV1012 | Unknown native event (`onclick`, `onTransitionend`) | `on` + PascalCase |
411
- | RV1015 | Handler is a literal, not a function | pass a function |
412
- | RV1016 | Handler expression reads state when it binds | wrap in an arrow |
413
- | RV1101 | Unsafe / unsupported attribute or URL (`innerHTML`, `javascript:`) | remove; build-time assets |
414
- | RV1102 | Invalid `preload` value | `eager` / `idle` / `visible` / `intent` / `interaction` |
415
- | RV2001 | Browser global read during server rendering | handler / `clientEffect` / `clientEvent` / `clientOnly` |
416
- | RV2002 | Ref callback must assign a local | `ref={(el) => (local = el)}` or `ref()` |
417
- | RV2003 | Captured class instance | pass data; inside handler; `reconstruct(…)` |
418
- | RV2102 | Captured instance of a module class that is not a data class (`#private`, base, resource) | inside handler; `reconstruct(…)`; plain fields |
419
- | RV2006 | JSX inside handler / effect / computed | JSX in component + signal |
420
- | RV2007 | Resumable code captures `this` | `event.currentTarget` |
421
- | RV2009 | Two module references share a browser name | rename one import |
422
- | RV2011 | Mutually recursive functions split by an early exit | move them together before the exit |
423
- | RV2012–RV2014 | (warning; error on a provable function) `.bind` that may not resume | `const g = f.bind(t, …)`; arrow for other callees |
424
- | RV2015 | (warning) component returns a function that cannot resume (anon default / wrapper / `let`) | `export function Name(props) { return () => …; }` |
425
- | RV2101 | (warning) captured value not serializable (type-aware) | as RV2003 |
426
- | RV3003 / RV3004 | Structural helper namespace import / alias | `import { For } from "@rivetjs/runtime/dom"` |
427
- | RV3006 | `clientOnly` erasure shape | inline `() => import("…")` |
428
- | RV3007 | Route module server export shape (`export *` in a route) | declare server exports directly |
429
- | RV3008 | Server-only code reached from browser code | keep in `serverLoad` / `*.server.ts` |
430
- | RV3101 | (warning) Link matches no route | fix the path |
431
- | RV4001–4004 | i18n key / param / argument / tag errors | see section 5 |
432
- | RV5001–5003 | ``css`…` `` syntax / usage; invalid or unsafe class / style (style string, two class attrs) | style object, one `class` |
433
- | RV6001–6004 | Slot errors (`<slot>`, `r:name`, `r:slot`) | see JSX reference, slots |
434
- | RV7101 / RV7102 | `.rivet/editor.json` missing / stale | `rivet sync` |
435
- | RV9000 / RV9001 | Unclassified / internal compiler error | report it |
407
+ | Code | Meaning | Fix |
408
+ | --------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
409
+ | RV1000 | Parse error | fix syntax |
410
+ | RV1005 | HTML nesting the parser would repair (`<div>` in `<p>`) | valid nesting |
411
+ | RV1012 | Unknown native event (`onclick`, `onTransitionend`) | `on` + PascalCase |
412
+ | RV1015 | Handler is a literal, not a function | pass a function |
413
+ | RV1016 | Handler expression reads state when it binds | wrap in an arrow |
414
+ | RV1101 | Unsafe / unsupported attribute or URL (`innerHTML`, `javascript:`) | remove; build-time assets |
415
+ | RV1102 | Invalid `preload` value | `eager` / `idle` / `visible` / `intent` / `interaction` |
416
+ | RV2001 | Browser global read during server rendering | handler / `clientEffect` / `clientEvent` / `clientOnly` |
417
+ | RV2002 | Ref callback must assign a local | `ref={(el) => (local = el)}` or `ref()` |
418
+ | RV2003 | Captured class instance | pass data; inside handler; `reconstruct(…)` |
419
+ | RV2102 | Captured instance of a module class that is not a data class (`#private`, base, resource) | inside handler; `reconstruct(…)`; plain fields |
420
+ | RV2006 | JSX inside handler / effect / computed | JSX in component + signal |
421
+ | RV2007 | Resumable code captures `this` | `event.currentTarget` |
422
+ | RV2009 | Two module references share a browser name | rename one import |
423
+ | RV2011 | Mutually recursive functions split by an early exit | move them together before the exit |
424
+ | RV2012–RV2014 | (warning; error on a provable function) `.bind` that may not resume | `const g = f.bind(t, …)`; arrow for other callees |
425
+ | RV2015 | (warning) component returns a function that cannot resume (anon default / wrapper / `let`) | `export function Name(props) { return () => …; }` |
426
+ | RV2016 | (warning) `resource()` that cannot resume (destructured, `let`, not a `const` initializer) | one `const user = resource(…)`; quick fix |
427
+ | RV2101 | (warning) captured value not serializable (type-aware) | as RV2003 |
428
+ | RV2103 / RV2104 | `resume: { model: "derived" }` only: `Date.now()` / `Math.random()` while rendering / (warning) signal created in a loop | keyed `signal(Date.now())` or `clientEffect`; one signal outside the loop |
429
+ | RV3003 / RV3004 | Structural helper namespace import / alias | `import { For } from "@rivetjs/runtime/dom"` |
430
+ | RV3006 | `clientOnly` erasure shape | inline `() => import("…")` |
431
+ | RV3007 | Route module server export shape (`export *` in a route) | declare server exports directly |
432
+ | RV3008 | Server-only code reached from browser code | keep in `load(…)` / `*.server.ts` |
433
+ | RV3011 | Server-only module reached from a component, layout or error page (router build) | pass it as props from the page's `load(…)` |
434
+ | RV3101 | (warning) Link matches no route | fix the path |
435
+ | RV4001–4004 | i18n key / param / argument / tag errors | see section 5 |
436
+ | RV5001–5003 | ``css`…` `` syntax / usage; invalid or unsafe class / style (style string, two class attrs) | style object, one `class` |
437
+ | RV6001–6004 | Slot errors (`<slot>`, `r:name`, `r:slot`) | see JSX reference, slots |
438
+ | RV7101 / RV7102 | `.rivet/editor.json` missing / stale | `rivet sync` |
439
+ | RV9000 / RV9001 | Unclassified / internal compiler error | report it |
436
440
 
437
441
  - Not a code: render-time `… is not serializable` (see section 3).
438
442