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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/templates/starter-minimal/.agents/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-minimal/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-minimal/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-minimal/.claude/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-minimal/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-minimal/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-minimal/.cursor/rules/rivet-add-route.mdc +9 -9
- package/templates/starter-minimal/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
- package/templates/starter-minimal/.cursor/rules/rivet-port-react-component.mdc +1 -1
- package/templates/starter-minimal/.cursor/rules/rivet.mdc +1 -1
- package/templates/starter-minimal/.github/copilot-instructions.md +1 -1
- package/templates/starter-minimal/AGENTS.md +50 -46
- package/templates/starter-minimal/CLAUDE.md +50 -46
- package/templates/starter-minimal/README.md +1 -1
- package/templates/starter-server/.agents/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-server/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-server/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-server/.claude/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-server/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-server/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-server/.cursor/rules/rivet-add-route.mdc +9 -9
- package/templates/starter-server/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
- package/templates/starter-server/.cursor/rules/rivet-port-react-component.mdc +1 -1
- package/templates/starter-server/.cursor/rules/rivet.mdc +1 -1
- package/templates/starter-server/.github/copilot-instructions.md +1 -1
- package/templates/starter-server/AGENTS.md +50 -46
- package/templates/starter-server/CLAUDE.md +50 -46
- package/templates/starter-server/README.md +3 -3
- package/templates/starter-server/src/routes/index.tsx +5 -5
- package/templates/starter-server/vite.config.ts +1 -1
- package/templates/starter-static/.agents/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-static/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-static/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-static/.claude/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-static/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-static/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-static/.cursor/rules/rivet-add-route.mdc +9 -9
- package/templates/starter-static/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
- package/templates/starter-static/.cursor/rules/rivet-port-react-component.mdc +1 -1
- package/templates/starter-static/.cursor/rules/rivet.mdc +1 -1
- package/templates/starter-static/.github/copilot-instructions.md +1 -1
- package/templates/starter-static/AGENTS.md +50 -46
- package/templates/starter-static/CLAUDE.md +50 -46
- package/templates/starter-static/README.md +1 -1
- package/templates/starter-ui/.agents/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-ui/.agents/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-ui/.agents/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-ui/.claude/skills/rivet-add-route/SKILL.md +9 -9
- package/templates/starter-ui/.claude/skills/rivet-form-in-drawer/SKILL.md +6 -5
- package/templates/starter-ui/.claude/skills/rivet-port-react-component/SKILL.md +1 -1
- package/templates/starter-ui/.cursor/rules/rivet-add-route.mdc +9 -9
- package/templates/starter-ui/.cursor/rules/rivet-form-in-drawer.mdc +6 -5
- package/templates/starter-ui/.cursor/rules/rivet-port-react-component.mdc +1 -1
- package/templates/starter-ui/.cursor/rules/rivet.mdc +1 -1
- package/templates/starter-ui/.github/copilot-instructions.md +1 -1
- package/templates/starter-ui/AGENTS.md +50 -46
- package/templates/starter-ui/CLAUDE.md +50 -46
- 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
|
|
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/
|
|
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,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 (
|
|
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
|
|
11
|
-
2. Write the page. Server code lives in `
|
|
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
|
|
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
|
|
31
|
-
- Access checks: `
|
|
32
|
-
- Static mode (`rivetRouter({ mode: "static" })`): `
|
|
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
|
|
30
|
-
export const
|
|
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
|
|
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 `
|
|
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
|
|
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 (
|
|
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
|
|
11
|
-
2. Write the page. Server code lives in `
|
|
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
|
|
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
|
|
31
|
-
- Access checks: `
|
|
32
|
-
- Static mode (`rivetRouter({ mode: "static" })`): `
|
|
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
|
|
30
|
-
export const
|
|
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
|
|
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 `
|
|
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
|
|
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 (
|
|
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
|
|
12
|
-
2. Write the page. Server code lives in `
|
|
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
|
|
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
|
|
32
|
-
- Access checks: `
|
|
33
|
-
- Static mode (`rivetRouter({ mode: "static" })`): `
|
|
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
|
|
31
|
-
export const
|
|
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
|
|
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 `
|
|
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
|
|
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 `
|
|
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 `
|
|
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`,
|
|
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
|
|
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
|
|
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
|
|
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: `
|
|
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
|
|
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
|
|
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`). `
|
|
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
|
|
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
|
|
407
|
-
| --------------- |
|
|
408
|
-
| RV1000 | Parse error
|
|
409
|
-
| RV1005 | HTML nesting the parser would repair (`<div>` in `<p>`)
|
|
410
|
-
| RV1012 | Unknown native event (`onclick`, `onTransitionend`)
|
|
411
|
-
| RV1015 | Handler is a literal, not a function
|
|
412
|
-
| RV1016 | Handler expression reads state when it binds
|
|
413
|
-
| RV1101 | Unsafe / unsupported attribute or URL (`innerHTML`, `javascript:`)
|
|
414
|
-
| RV1102 | Invalid `preload` value
|
|
415
|
-
| RV2001 | Browser global read during server rendering
|
|
416
|
-
| RV2002 | Ref callback must assign a local
|
|
417
|
-
| RV2003 | Captured class instance
|
|
418
|
-
| RV2102 | Captured instance of a module class that is not a data class (`#private`, base, resource)
|
|
419
|
-
| RV2006 | JSX inside handler / effect / computed
|
|
420
|
-
| RV2007 | Resumable code captures `this`
|
|
421
|
-
| RV2009 | Two module references share a browser name
|
|
422
|
-
| RV2011 | Mutually recursive functions split by an early exit
|
|
423
|
-
| RV2012–RV2014 | (warning; error on a provable function) `.bind` that may not resume
|
|
424
|
-
| RV2015 | (warning) component returns a function that cannot resume (anon default / wrapper / `let`)
|
|
425
|
-
|
|
|
426
|
-
|
|
|
427
|
-
|
|
|
428
|
-
|
|
|
429
|
-
|
|
|
430
|
-
|
|
|
431
|
-
|
|
|
432
|
-
|
|
|
433
|
-
|
|
|
434
|
-
|
|
|
435
|
-
|
|
|
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
|
|