Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions docs/guide/edge/prerendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ Pages without dynamic params do not need `getPrerenderPaths()`.

## Dynamic pages (with params)

For pages with URL params, also export `getPrerenderPaths()` to specify which param combinations to prerender:
For pages with URL params, also export `getPrerenderPaths()` to specify which param combinations to prerender. Annotate the return type with [`PageParams`](../../reference/api/types.md#route-types) for that page's URL pattern, such as `/blog/:slug` for `pages/blog/[slug].server.ts`. TypeScript then requires each object to include those params, so a missing or misspelled key is a type error. Void does not infer this type from the filename.

```ts
// pages/blog/[slug].server.ts
import type { PageParams } from 'void/routes';

export const prerender = true;

export async function getPrerenderPaths() {
// Return param objects matching the URL pattern
export async function getPrerenderPaths(): Promise<PageParams<'/blog/:slug'>[]> {
return [{ slug: 'hello-world' }, { slug: 'getting-started' }];
}
```
Expand All @@ -42,11 +43,13 @@ In custom SSR mode (`src/main.ssr.ts`), export `getPrerenderPaths()` at the top
// src/main.ssr.ts
export const prerender = true;

export async function getPrerenderPaths() {
export async function getPrerenderPaths(): Promise<string[]> {
return ['/', '/about', '/blog/hello-world'];
}
```

Annotate this return type as an array of path strings. Use `PageParams` on file-based pages, where the function returns param objects.

## Relationship to revalidation

Setting `routing.isr: false` in `void.config.ts` disables ISR and this edge-prerendering behavior, including per-page exports. It does not disable build-time HTML generation with `output: "static"`.
Expand Down
6 changes: 4 additions & 2 deletions docs/guide/ssg.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ Individual pages can opt out of prerendering:
export const prerender = false;
```

Dynamic pages with `getPrerenderPaths()` are prerendered for the returned param combinations:
Dynamic pages with `getPrerenderPaths()` are prerendered for the returned param combinations. Annotate the return type with [`PageParams`](../reference/api/types.md#route-types) so TypeScript checks the param names. Void does not infer them from the filename:

```ts
// pages/blog/[slug].server.ts
export async function getPrerenderPaths() {
import type { PageParams } from 'void/routes';

export async function getPrerenderPaths(): Promise<PageParams<'/blog/:slug'>[]> {
return [{ slug: 'hello-world' }, { slug: 'getting-started' }];
}
```
Expand Down
36 changes: 26 additions & 10 deletions docs/guide/type-safety.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@ The pattern must be one of your pages; HTTP and WebSocket routes are not accepte

The pattern is checked by TypeScript only. Pass the pattern of the page the component renders in; without a pattern, `useParams()` returns `Record<string, string>`.

### Prerender paths

On a dynamic page, annotate `getPrerenderPaths()` with `PageParams` for that page's URL pattern. Each returned object must then include the page's params, and a missing or misspelled key is a type error. Void does not infer this type from the filename, so the check applies only when the annotation is present:

```ts
// pages/blog/[slug].server.ts
import type { PageParams } from 'void/routes';

export async function getPrerenderPaths(): Promise<PageParams<'/blog/:slug'>[]> {
return [{ slug: 'hello-world' }];
}
```

In custom SSR mode the function returns full path strings, so annotate it as `Promise<string[]>`. See [Edge Prerendering](./edge/prerendering.md#dynamic-pages-with-params) and [Static Site Generation](./ssg.md#per-page-overrides).

### Checked URL strings

[`routing.typedRoutes`](../reference/config.md#routing-typedroutes) checks plain URL strings in `Link`'s `href`, `router.visit()`, `router.prefetch()`, `router.flush()`, and `c.rewrite()`. Apps created with `void init` have it on. To turn it on in an existing app:
Expand Down Expand Up @@ -201,16 +216,17 @@ Route types update with the dev server. For CI or standalone typechecking, run `

## What Gets Checked

| Layer | What's type-checked |
| ------------ | --------------------------------------------------------------------------------------------------------------------- |
| Database | Table columns, value types, insert/update shapes from Drizzle schema |
| Handlers | `c.env` bindings, validator input, return type |
| Fetch client | Route paths, HTTP methods, `body`/`query`/`params`, response type |
| Navigation | Route patterns and required parameter names and values in `routeUrl()` and destination objects |
| Links | With `routing.typedRoutes`, URL strings in `Link`, router methods, and `c.rewrite()` |
| `useParams` | Page patterns and the names of the params they return |
| `useForm` | Action URLs, `data` fields, `errors` keys, `reset()` args (including `{ initial: true }`), `params`, `resetOnSuccess` |
| `action()` | Action URLs, `data` payload, `params` |
| Layer | What's type-checked |
| ------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Database | Table columns, value types, insert/update shapes from Drizzle schema |
| Handlers | `c.env` bindings, validator input, return type |
| Fetch client | Route paths, HTTP methods, `body`/`query`/`params`, response type |
| Navigation | Route patterns and required parameter names and values in `routeUrl()` and destination objects |
| Links | With `routing.typedRoutes`, URL strings in `Link`, router methods, and `c.rewrite()` |
| `useParams` | Page patterns and the names of the params they return |
| `getPrerenderPaths` | Page param names, when the return type is annotated with `PageParams` |
| `useForm` | Action URLs, `data` fields, `errors` keys, `reset()` args (including `{ initial: true }`), `params`, `resetOnSuccess` |
| `action()` | Action URLs, `data` payload, `params` |

## Serialization

Expand Down
2 changes: 2 additions & 0 deletions docs/reference/api/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,6 @@ Import these types from `void/routes`:
| `RouteQuery` | Query keys mapped to `RouteQueryValue` or readonly arrays of those values. |
| `RouteQueryValue` | `string`, `number`, `boolean`, `null`, or `undefined`. |

Annotate a dynamic page's `getPrerenderPaths()` as `Promise<PageParams<P>[]>` so TypeScript checks those param names. See [Prerender paths](../../guide/type-safety.md#prerender-paths).

`typedRoutes` is [`routing.typedRoutes`](../config.md#routing-typedroutes). See [Checked URL strings](../../guide/type-safety.md#checked-url-strings).
2 changes: 1 addition & 1 deletion docs/reference/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ Output mode. Controls the default rendering strategy for pages.
| `"server"` (default) | `false` | `export const prerender = true` | Deploy-time (platform ISR) |
| `"static"` | `true` | `export const prerender = false` | Build or deploy post-build |

When set to `"static"`, pages are prerendered to HTML in `dist/client/`. Individual pages can opt out with `export const prerender = false`. Dynamic pages need `getPrerenderPaths()` to be prerendered.
When set to `"static"`, pages are prerendered to HTML in `dist/client/`. Individual pages can opt out with `export const prerender = false`. Dynamic pages need [`getPrerenderPaths()`](../guide/ssg.md#per-page-overrides) to be prerendered. Annotate its return type with [`PageParams`](./api/types.md#route-types) so TypeScript checks the param names.

When omitted or set to `"server"`, pages are server-rendered on request. Individual pages can opt into deploy-time prerendering with `export const prerender = true`, or opt out of server-rendered component HTML with `export const ssr = false`.

Expand Down
Loading