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
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Deploy hosted production before publishing the npm packages: the CLI checks the

- **Do not use Python for repository scripts or automation**, including inline CI scripts and ad-hoc maintenance commands. Use Node.js with TypeScript/JavaScript, or shell/PowerShell where appropriate.
- **Keep user-facing documentation focused on using Void.** Explain public APIs, configuration, observable behavior, and actions users need to take. Do not add implementation details, internal workarounds, defensive checks, normalization rules, or bug-fix history unless they help users make a decision or resolve an actionable problem. Maintainer-only knowledge belongs in source comments, repository/package `AGENTS.md`, or contributor guides. This applies to app guides, API/CLI references, consumer-facing READMEs, and shipped app-development skills.
- **Keep Pages examples framework-agnostic.** Void Pages supports React, Vue, Svelte, and Solid. When a docs example uses an adapter API and the syntax differs, show all four in a `::: code-group` with those tab labels. When the code is the same, write it once; if only the import path differs, name the other packages in a comment (`@void/react`, `@void/vue`, `@void/svelte`, `@void/solid`). Do not use one framework's component syntax, import, or file extension as the only example. Keep a single framework when the section is that framework's own API, a labeled custom SSR renderer, a JSX route handler, or an integration guide. Do not wrap a one-snippet example in a code group.
- **Keep native Cloudflare deploys Workers Free-compatible by default.** New public Void SDK/runtime features must work with `void deploy --platform cloudflare` on Workers Free unless the underlying Cloudflare product has no Free offering. Paid-only functionality must be explicitly opt-in, inferred and emitted only when the app uses it, and checked before provisioning or building. Its documentation and deploy error must name the required Cloudflare product/plan, link to enable it, and explain any API-token permissions. Platform-internal paid services must never become requirements of a single-app native Cloudflare deploy. Add regression coverage proving that an app which does not use the paid feature emits no paid binding and performs no entitlement probe.
- Every implementation plan should include the following:
- During plan generation: ask clarifying questions when necessary, especially around developer-experience defining design decisions.
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/pages-routing/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,13 @@ Welcome to Void. Here's an interactive demo:
<Counter />
```

- **`<script>` block:** extracted before markdown compilation. Island imports use `with { island: "..." }` syntax (see [islands](./islands)). Any other code in the block becomes a [client script](#client-scripts) that runs in the browser.
- **`<script>` block:** extracted before markdown compilation. Island imports use `with { island: "..." }` syntax (see [islands](./islands)). The component file uses your framework's extension (`.vue`, `.tsx`, or `.svelte`). Any other code in the block becomes a [client script](#client-scripts) that runs in the browser.
- **Frontmatter:** YAML metadata between `---` fences. Layouts and islands can read it through [`useFrontmatter()`](#frontmatter-access).
- **Body:** standard markdown plus GFM. Uppercase tags such as `<Counter />` reference imported components and render as islands.

## Client Scripts

Use the `<script>` block for island imports and JavaScript that runs when the page loads:
Use the `<script>` block for island imports and JavaScript that runs when the page loads. Import the component with your framework's extension (`.vue`, `.tsx`, or `.svelte`):

```md
<script>
Expand Down
117 changes: 115 additions & 2 deletions docs/guide/pages-routing/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,80 @@ With [`routing.typedRoutes`](../type-safety.md#checked-url-strings), which apps

`Link` also supports query data, history replacement, full-page navigation, and cancellation:

```tsx
::: code-group

```tsx [React]
import { Link } from '@void/react';

<Link href="/users" data={{ page: 2, tag: ['active', 'new'] }}>
Filtered users
</Link>

<Link href="/users" replace>
Users
</Link>

<Link href="/logout" reloadDocument>
Sign out
</Link>

<Link
href="/settings"
onNavigate={(event) => {
if (!confirm('Leave this page?')) {
event.preventDefault();
}
}}
>
Settings
</Link>
```

```vue [Vue]
<script setup lang="ts">
import { Link } from '@void/vue';

function onNavigate(event: { preventDefault(): void }) {
if (!confirm('Leave this page?')) {
event.preventDefault();
}
}
</script>

<template>
<Link href="/users" :data="{ page: 2, tag: ['active', 'new'] }">Filtered users</Link>
<Link href="/users" replace>Users</Link>
<Link href="/logout" reload-document>Sign out</Link>
<Link href="/settings" :on-navigate="onNavigate">Settings</Link>
</template>
```

```svelte [Svelte]
<script lang="ts">
import { Link } from '@void/svelte';
</script>

<Link href="/users" data={{ page: 2, tag: ['active', 'new'] }}>Filtered users</Link>

<Link href="/users" replace>Users</Link>

<Link href="/logout" reloadDocument>Sign out</Link>

<Link
href="/settings"
onNavigate={(event) => {
if (!confirm('Leave this page?')) {
event.preventDefault();
}
}}
>
Settings
</Link>
```

```tsx [Solid]
import { Link } from '@void/solid';

<Link href="/users" data={{ page: 2, tag: ['active', 'new'] }}>
Filtered users
</Link>
Expand All @@ -228,6 +301,8 @@ With [`routing.typedRoutes`](../type-safety.md#checked-url-strings), which apps
</Link>
```

:::

`prefetch` and `reloadDocument` work only with GET links. GET `data` becomes query parameters; use scalar values or arrays.

For programmatic navigation, use `useRouter()`. Pass a typed destination object to `visit()`, `prefetch()`, or `flush()`:
Expand Down Expand Up @@ -256,7 +331,9 @@ You can also pass URL strings or `URL` instances to these router methods. See [T

For dynamic route params, use `useParams()` from the same adapter package. Pass the page's URL pattern as the type argument to type the result:

```tsx
::: code-group

```tsx [React]
// pages/posts/[id].tsx
import { useParams } from '@void/react';

Expand All @@ -266,6 +343,42 @@ export default function PostPage() {
}
```

```vue [Vue]
<!-- pages/posts/[id].vue -->
<script setup lang="ts">
import { useParams } from '@void/vue';

const params = useParams<'/posts/:id'>(); // { id: string }
</script>

<template>
<h1>Post {{ params.id }}</h1>
</template>
```

```svelte [Svelte]
<!-- pages/posts/[id].svelte -->
<script lang="ts">
import { useParams } from '@void/svelte';

const params = useParams<'/posts/:id'>(); // { id: string }
</script>

<h1>Post {params.id}</h1>
```

```tsx [Solid]
// pages/posts/[id].tsx
import { useParams } from '@void/solid';

export default function PostPage() {
const params = useParams<'/posts/:id'>(); // { id: string }
return <h1>Post {params.id}</h1>;
}
```

:::

See [Page params](../type-safety.md#page-params) for how patterns are checked.

### Active links
Expand Down
Loading
Loading