From ae94680dae70801b4405ff459860b10eb473a56e Mon Sep 17 00:00:00 2001 From: Yusuke Wada Date: Fri, 6 Nov 2026 17:20:07 +0900 Subject: [PATCH 1/2] feat(hono): add the Hono JSX adapter for Pages --- .github/scripts/packed-consumer-scope.mjs | 2 +- .../scripts/packed-consumer-scope.test.mjs | 1 + .github/scripts/packed-consumer.mjs | 12 +- .github/scripts/packed-init.mjs | 2 +- AGENTS.md | 3 +- README.md | 2 +- TESTING.md | 5 +- docs/guide/app-types.md | 2 +- docs/guide/index.md | 2 +- docs/guide/pages-routing/actions-and-forms.md | 49 +++- docs/guide/pages-routing/islands.md | 56 ++++ docs/guide/pages-routing/layouts.md | 30 +- docs/guide/pages-routing/loaders.md | 39 ++- docs/guide/pages-routing/markdown.md | 30 ++ docs/guide/pages-routing/overview.md | 46 ++- docs/guide/pages-routing/view-transitions.md | 23 ++ docs/index.md | 2 +- docs/reference/api.md | 2 +- docs/reference/api/pages.md | 73 ++++- docs/reference/cli/setup.md | 2 +- docs/reference/structure.md | 2 +- package.json | 1 + packages/create-void/README.md | 2 +- packages/hono/README.md | 42 +++ packages/hono/package.json | 59 ++++ packages/hono/src/index.ts | 1 + packages/hono/src/plugin-islands.ts | 266 ++++++++++++++++++ packages/hono/src/plugin-pages.ts | 210 ++++++++++++++ packages/hono/src/plugin.ts | 38 +++ packages/hono/src/runtime/action.ts | 6 + packages/hono/src/runtime/context.ts | 23 ++ packages/hono/src/runtime/form.tsx | 54 ++++ packages/hono/src/runtime/index.ts | 19 ++ packages/hono/src/runtime/link.tsx | 190 +++++++++++++ packages/hono/src/runtime/pages-client.tsx | 204 ++++++++++++++ packages/hono/src/runtime/pages-server.ts | 153 ++++++++++ .../hono/src/runtime/submission-status.ts | 53 ++++ packages/hono/src/runtime/use-form.ts | 179 ++++++++++++ packages/hono/src/runtime/use-island-form.ts | 100 +++++++ packages/hono/src/runtime/use-navigation.ts | 7 + packages/hono/src/runtime/use-params.ts | 21 ++ packages/hono/src/runtime/use-router.ts | 8 + packages/hono/src/runtime/use-shared.ts | 11 + .../integration/hono-pages-playground.test.ts | 245 ++++++++++++++++ .../test/integration/island-css-build.test.ts | 94 +++++++ .../integration/md-layout-frontmatter.test.ts | 163 +++++++++++ .../hono/test/unit/form-errors.typecheck.ts | 45 +++ .../hono/test/unit/island-form.typecheck.ts | 37 +++ packages/hono/test/unit/link.test.ts | 134 +++++++++ packages/hono/test/unit/pages-client.test.ts | 127 +++++++++ packages/hono/test/unit/pages-server.test.ts | 162 +++++++++++ .../hono/test/unit/plugin-islands.test.ts | 231 +++++++++++++++ packages/hono/test/unit/plugin-pages.test.ts | 170 +++++++++++ packages/hono/test/unit/plugin.test.ts | 29 ++ packages/hono/tsconfig.json | 20 ++ packages/hono/vite.config.ts | 19 ++ packages/md/README.md | 2 +- packages/md/src/emit-hono.ts | 70 +++++ packages/md/src/plugin.ts | 20 +- packages/md/test/unit/emit-hono.test.ts | 37 +++ packages/void/README.md | 2 +- packages/void/src/cli/help.ts | 5 +- packages/void/src/cli/init.ts | 3 + packages/void/src/cli/template.ts | 46 ++- packages/void/templates/assets/hono.svg | 10 + packages/void/test/unit/init.test.ts | 9 +- .../test/unit/pages-plugin-adapters.test.ts | 2 + packages/void/test/unit/template.test.ts | 45 ++- packages/void/vite.config.ts | 1 + playground/hono-pages/middleware/01.shared.ts | 20 ++ playground/hono-pages/package.json | 22 ++ .../hono-pages/pages/blog/[slug].island.tsx | 13 + .../hono-pages/pages/blog/[slug].server.ts | 15 + playground/hono-pages/pages/blog/_Counter.tsx | 11 + .../hono-pages/pages/blog/_PostForm.tsx | 36 +++ playground/hono-pages/pages/blog/_data.ts | 4 + .../hono-pages/pages/blog/index.island.tsx | 25 ++ .../hono-pages/pages/blog/index.server.ts | 21 ++ .../hono-pages/pages/deferred.server.ts | 14 + playground/hono-pages/pages/deferred.tsx | 16 ++ .../hono-pages/pages/form-errors.server.ts | 12 + playground/hono-pages/pages/form-errors.tsx | 69 +++++ .../hono-pages/pages/form-reset.server.ts | 8 + playground/hono-pages/pages/form-reset.tsx | 50 ++++ playground/hono-pages/pages/index.server.ts | 9 + playground/hono-pages/pages/index.tsx | 15 + playground/hono-pages/pages/layout.tsx | 30 ++ .../hono-pages/pages/link-behavior.server.ts | 25 ++ playground/hono-pages/pages/link-behavior.tsx | 129 +++++++++ .../pages/scroll-restoration.server.ts | 8 + .../hono-pages/pages/scroll-restoration.tsx | 40 +++ playground/hono-pages/pages/twins/_shared.tsx | 12 + playground/hono-pages/pages/twins/a.tsx | 1 + playground/hono-pages/pages/twins/b.tsx | 1 + .../hono-pages/pages/users/[id].server.ts | 17 ++ playground/hono-pages/pages/users/[id].tsx | 14 + playground/hono-pages/pages/users/_data.ts | 4 + .../hono-pages/pages/users/index.server.ts | 20 ++ playground/hono-pages/pages/users/index.tsx | 49 ++++ playground/hono-pages/routes/api/health.ts | 5 + playground/hono-pages/tsconfig.json | 17 ++ playground/hono-pages/vite.config.ts | 9 + playground/hono-pages/void.config.ts | 11 + pnpm-lock.yaml | 50 ++++ test/integration-timings.ts | 1 + test/private-pages-build.ts | 13 +- vite.config.ts | 1 + 107 files changed, 4532 insertions(+), 45 deletions(-) create mode 100644 packages/hono/README.md create mode 100644 packages/hono/package.json create mode 100644 packages/hono/src/index.ts create mode 100644 packages/hono/src/plugin-islands.ts create mode 100644 packages/hono/src/plugin-pages.ts create mode 100644 packages/hono/src/plugin.ts create mode 100644 packages/hono/src/runtime/action.ts create mode 100644 packages/hono/src/runtime/context.ts create mode 100644 packages/hono/src/runtime/form.tsx create mode 100644 packages/hono/src/runtime/index.ts create mode 100644 packages/hono/src/runtime/link.tsx create mode 100644 packages/hono/src/runtime/pages-client.tsx create mode 100644 packages/hono/src/runtime/pages-server.ts create mode 100644 packages/hono/src/runtime/submission-status.ts create mode 100644 packages/hono/src/runtime/use-form.ts create mode 100644 packages/hono/src/runtime/use-island-form.ts create mode 100644 packages/hono/src/runtime/use-navigation.ts create mode 100644 packages/hono/src/runtime/use-params.ts create mode 100644 packages/hono/src/runtime/use-router.ts create mode 100644 packages/hono/src/runtime/use-shared.ts create mode 100644 packages/hono/test/integration/hono-pages-playground.test.ts create mode 100644 packages/hono/test/integration/island-css-build.test.ts create mode 100644 packages/hono/test/integration/md-layout-frontmatter.test.ts create mode 100644 packages/hono/test/unit/form-errors.typecheck.ts create mode 100644 packages/hono/test/unit/island-form.typecheck.ts create mode 100644 packages/hono/test/unit/link.test.ts create mode 100644 packages/hono/test/unit/pages-client.test.ts create mode 100644 packages/hono/test/unit/pages-server.test.ts create mode 100644 packages/hono/test/unit/plugin-islands.test.ts create mode 100644 packages/hono/test/unit/plugin-pages.test.ts create mode 100644 packages/hono/test/unit/plugin.test.ts create mode 100644 packages/hono/tsconfig.json create mode 100644 packages/hono/vite.config.ts create mode 100644 packages/md/src/emit-hono.ts create mode 100644 packages/md/test/unit/emit-hono.test.ts create mode 100644 packages/void/templates/assets/hono.svg create mode 100644 playground/hono-pages/middleware/01.shared.ts create mode 100644 playground/hono-pages/package.json create mode 100644 playground/hono-pages/pages/blog/[slug].island.tsx create mode 100644 playground/hono-pages/pages/blog/[slug].server.ts create mode 100644 playground/hono-pages/pages/blog/_Counter.tsx create mode 100644 playground/hono-pages/pages/blog/_PostForm.tsx create mode 100644 playground/hono-pages/pages/blog/_data.ts create mode 100644 playground/hono-pages/pages/blog/index.island.tsx create mode 100644 playground/hono-pages/pages/blog/index.server.ts create mode 100644 playground/hono-pages/pages/deferred.server.ts create mode 100644 playground/hono-pages/pages/deferred.tsx create mode 100644 playground/hono-pages/pages/form-errors.server.ts create mode 100644 playground/hono-pages/pages/form-errors.tsx create mode 100644 playground/hono-pages/pages/form-reset.server.ts create mode 100644 playground/hono-pages/pages/form-reset.tsx create mode 100644 playground/hono-pages/pages/index.server.ts create mode 100644 playground/hono-pages/pages/index.tsx create mode 100644 playground/hono-pages/pages/layout.tsx create mode 100644 playground/hono-pages/pages/link-behavior.server.ts create mode 100644 playground/hono-pages/pages/link-behavior.tsx create mode 100644 playground/hono-pages/pages/scroll-restoration.server.ts create mode 100644 playground/hono-pages/pages/scroll-restoration.tsx create mode 100644 playground/hono-pages/pages/twins/_shared.tsx create mode 100644 playground/hono-pages/pages/twins/a.tsx create mode 100644 playground/hono-pages/pages/twins/b.tsx create mode 100644 playground/hono-pages/pages/users/[id].server.ts create mode 100644 playground/hono-pages/pages/users/[id].tsx create mode 100644 playground/hono-pages/pages/users/_data.ts create mode 100644 playground/hono-pages/pages/users/index.server.ts create mode 100644 playground/hono-pages/pages/users/index.tsx create mode 100644 playground/hono-pages/routes/api/health.ts create mode 100644 playground/hono-pages/tsconfig.json create mode 100644 playground/hono-pages/vite.config.ts create mode 100644 playground/hono-pages/void.config.ts diff --git a/.github/scripts/packed-consumer-scope.mjs b/.github/scripts/packed-consumer-scope.mjs index 3ea13b683..df2cef42a 100644 --- a/.github/scripts/packed-consumer-scope.mjs +++ b/.github/scripts/packed-consumer-scope.mjs @@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url'; const fullSuitePaths = [ /^(?:package\.json|pnpm-lock\.yaml|pnpm-workspace\.yaml|\.node-version|\.npmrc|\.pnpmfile\.cjs|vite\.config\.[jt]s)$/, /^packages\/[^/]+\/(?:package\.json|(?:vite|tsdown)\.config\.[cm]?[jt]s|tsconfig[^/]*\.json)$/, - /^packages\/(?:create-void|react|vue|svelte|solid|md)\/(?:src|bin)\//, + /^packages\/(?:create-void|react|vue|svelte|solid|hono|md)\/(?:src|bin)\//, /^packages\/void\/src\/cli\/(?:init|template|package|agents)(?:[-./])/, /^\.github\/scripts\/(?:packed-|release-policy)/, /^\.github\/workflows\/(?:packed-consumers|prepublish-test|publish)\.yml$/, diff --git a/.github/scripts/packed-consumer-scope.test.mjs b/.github/scripts/packed-consumer-scope.test.mjs index 27846a316..3361add79 100644 --- a/.github/scripts/packed-consumer-scope.test.mjs +++ b/.github/scripts/packed-consumer-scope.test.mjs @@ -28,6 +28,7 @@ test('runs the full matrix for dependencies, adapters, scaffolding, and packagin 'packages/void/package.json', 'packages/void/vite.config.ts', 'packages/solid/src/plugin.ts', + 'packages/hono/src/plugin.ts', 'packages/vue/src/runtime/index.ts', 'packages/create-void/src/pm.ts', 'packages/void/src/cli/init.ts', diff --git a/.github/scripts/packed-consumer.mjs b/.github/scripts/packed-consumer.mjs index 94778ffe0..feb07452e 100644 --- a/.github/scripts/packed-consumer.mjs +++ b/.github/scripts/packed-consumer.mjs @@ -149,9 +149,11 @@ assert(managers.every((manager) => ['npm', 'pnpm'].includes(manager))); const frameworks = process.env.VOID_PACKED_FRAMEWORK ? [process.env.VOID_PACKED_FRAMEWORK] : full - ? ['react', 'vue', 'svelte', 'solid'] + ? ['react', 'vue', 'svelte', 'solid', 'hono'] : ['react']; -assert(frameworks.every((framework) => ['react', 'vue', 'svelte', 'solid'].includes(framework))); +assert( + frameworks.every((framework) => ['react', 'vue', 'svelte', 'solid', 'hono'].includes(framework)), +); const pages = { react: `import { useState } from 'react'; import { Link } from '@void/react'; @@ -175,6 +177,12 @@ import { Link } from '@void/solid'; export default function Page(props: { message: string }) { const [count, setCount] = createSignal(0); return <>

{props.message}

Next page; +}`, + hono: `import { useState } from 'hono/jsx'; +import { Link } from '@void/hono'; +export default function Page({ message }: { message: string }) { + const [count, setCount] = useState(0); + return <>

{message}

Next page; }`, }; diff --git a/.github/scripts/packed-init.mjs b/.github/scripts/packed-init.mjs index 117b4e496..88592498c 100644 --- a/.github/scripts/packed-init.mjs +++ b/.github/scripts/packed-init.mjs @@ -122,7 +122,7 @@ export async function initializePackedProject( version, toolchain = 'vite', ) { - const frameworks = ['react', 'vue', 'svelte', 'solid']; + const frameworks = ['react', 'vue', 'svelte', 'solid', 'hono']; assert(frameworks.includes(framework), 'Unknown Pages framework'); assert(['npm', 'pnpm'].includes(manager), 'Unknown package manager'); assert(['vite', 'vite-plus'].includes(toolchain), 'Unknown toolchain'); diff --git a/AGENTS.md b/AGENTS.md index 760c468ae..78e4af404 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -73,6 +73,7 @@ pnpm workspaces managed through Vite+. Shared dependency versions use the **pnpm | [`packages/vue`](./packages/vue/README.md) | `@void/vue` | Vue framework adapter for Pages mode (SSR, Link, useForm) | | [`packages/svelte`](./packages/svelte/README.md) | `@void/svelte` | Svelte 5 framework adapter for Pages mode (SSR, Link, useForm) | | [`packages/solid`](./packages/solid/README.md) | `@void/solid` | SolidJS framework adapter for Pages mode (SSR, Link, useForm) | +| [`packages/hono`](./packages/hono/README.md) | `@void/hono` | Hono JSX framework adapter for Pages mode (SSR, Link, useForm) | | [`docs`](./docs) | (private) | Documentation site (VitePress) | | [`platform`](./platform/README.md) | (private) | Self-hosted platform source, routing, proxy, logs, and dashboard | | `playground/*` | (private) | local apps for testing | @@ -85,7 +86,7 @@ Platform packages live under `platform/packages/*` and may depend on public/shar Read [the core package instructions](./packages/void/AGENTS.md) for plugin, runtime, code-generation and CLI invariants. Each framework adapter has its -own README under `packages/{react,vue,svelte,solid}`. +own README under `packages/{react,vue,svelte,solid,hono}`. The [API reference](./docs/reference/api.md) and [CLI reference](./docs/reference/cli.md) describe the supported public surface. diff --git a/README.md b/README.md index d19824294..6e693be18 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Void brings together three open-source tools for you and your coding agents: - **Void CLI:** Build your app, provision the Cloudflare resources it uses, apply migrations, and deploy with one command. Manage resources, inspect logs, and roll back through the same CLI. - **Void Platform:** Install a shared deployment environment in your Cloudflare account for your team or customers, with access controls, resource limits, usage reporting, and [connected applications](./docs/guide/platform/administration/connected-apps.md) that request user-approved deployments. -The SDK's features are optional and composable. Use React, Vue, Svelte, or Solid for Void pages, or keep a [supported application framework](https://void.cloud/integrations/frameworks/overview) for routing and rendering while Void handles resource APIs, migrations, and deployment. +The SDK's features are optional and composable. Use React, Vue, Svelte, Solid, or Hono for Void pages, or keep a [supported application framework](https://void.cloud/integrations/frameworks/overview) for routing and rendering while Void handles resource APIs, migrations, and deployment. ## Getting Started diff --git a/TESTING.md b/TESTING.md index 7d9ed3435..25c136055 100644 --- a/TESTING.md +++ b/TESTING.md @@ -75,6 +75,7 @@ vp run test:integration vp run test:metaframeworks vp run test:pages:react-vue vp run test:pages:svelte-solid +vp run test:pages:hono vp run test:platform ``` @@ -213,7 +214,7 @@ workspace links can hide. They do not publish packages or deploy applications. Smoke coverage checks package contents and exports, real initialization, CLI help, a frozen reinstall, public Vite builds, TypeScript, and development HTTP requests for pages, assets, and -KV. It uses no browser. Full coverage adds Vue, Svelte, Solid, Markdown, production preview, +KV. It uses no browser. Full coverage adds Vue, Svelte, Solid, Hono, Markdown, production preview, and browser hydration/navigation. Superseded PR/main runs are cancelled; release runs are not. The selector reads all PR file pages or the complete pushed commit range. Renames consider both @@ -228,7 +229,7 @@ VOID_PACKED_SUITE=full node .github/scripts/packed-consumer.mjs node --test --test-timeout=10000 .github/scripts/packed-*.test.mjs ``` -`VOID_PACKED_PM=npm|pnpm` and `VOID_PACKED_FRAMEWORK=react|vue|svelte|solid` select a focused +`VOID_PACKED_PM=npm|pnpm` and `VOID_PACKED_FRAMEWORK=react|vue|svelte|solid|hono` select a focused local run. `VOID_PACKED_TOOLCHAIN=vite-plus` verifies Vite+ initialization, the workspace's tested Vite+ version, and the generated `vp` build/dev/preview commands. CI checks a React app with Vite+ for both package managers alongside the plain Vite coverage. diff --git a/docs/guide/app-types.md b/docs/guide/app-types.md index 24e91204c..189d5a225 100644 --- a/docs/guide/app-types.md +++ b/docs/guide/app-types.md @@ -14,7 +14,7 @@ The type is auto-detected from your project structure, or you can set it explici ## Void Apps -In a Void app, Void handles server routing and deployment. Use React, Vue, Svelte, or Solid for server-rendered pages, or build a frontend with any library that works with Vite. +In a Void app, Void handles server routing and deployment. Use React, Vue, Svelte, Solid, or Hono for server-rendered pages, or build a frontend with any library that works with Vite. A Void app can be **API-only** (just `routes/`), a **SPA + API** (frontend in `src/` with API routes), or **full-stack with pages mode** (server-rendered pages in `pages/` with co-located data loading): diff --git a/docs/guide/index.md b/docs/guide/index.md index 82bb3a0d3..0b0cade3a 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -4,7 +4,7 @@ outline: deep # What is Void? -Void is a full-stack framework for Cloudflare Workers, powered by Vite. Build your UI with React, Vue, Svelte, or Solid, and write server code in the same project. Void connects routing, data access, and deployment while keeping types shared across your app. +Void is a full-stack framework for Cloudflare Workers, powered by Vite. Build your UI with React, Vue, Svelte, Solid, or Hono, and write server code in the same project. Void connects routing, data access, and deployment while keeping types shared across your app. Start with the [Quickstart](./quickstart.md) to create and deploy an app. If you already use a meta framework, follow its [integration guide](../integrations/frameworks/overview.md). diff --git a/docs/guide/pages-routing/actions-and-forms.md b/docs/guide/pages-routing/actions-and-forms.md index 1b12747ce..8dc34e523 100644 --- a/docs/guide/pages-routing/actions-and-forms.md +++ b/docs/guide/pages-routing/actions-and-forms.md @@ -141,6 +141,28 @@ export default function CreateUser() { } ``` +```tsx [Hono] +// pages/users/index.tsx +import { Form, useForm } from '@void/hono'; + +export default function CreateUser() { + const form = useForm('/users', { name: '', email: '' }); + const value = (e: Event) => (e.target as HTMLInputElement).value; + + return ( +
+ form.setData('name', value(e))} /> + {form.errors.name && {form.errors.name}} + + form.setData('email', value(e))} /> + {form.errors.email && {form.errors.email}} + + +
+ ); +} +``` + ::: The types are inferred from your action's `withValidator()` schema in the companion `.server.ts` file. If no validator is defined, the body type falls back to `Record`, and you still get URL autocomplete. @@ -167,7 +189,7 @@ The types are inferred from your action's `withValidator()` schema in the compan Use `setError()` for client-side validation. Editing a field clears its error automatically. -For a native React form, use `form.post`, `form.put`, `form.patch`, or `form.delete` as a native form action. For an awaitable mutation, use `action()`. In Vue, Svelte, and Solid, form submissions return `Promise`; catch thrown errors or use your framework's error handling. +For a native React form, use `form.post`, `form.put`, `form.patch`, or `form.delete` as a native form action. For an awaitable mutation, use `action()`. In Vue, Svelte, Solid, and Hono, form submissions return `Promise`; catch thrown errors or use your framework's error handling. For dynamic routes, pass `params` in the options: @@ -200,7 +222,7 @@ const form = useForm( If a callback awaits `router.visit()` or `router.refresh()`, pass `{ resolveOn: 'data' }` so it can finish before React commits the navigation. See [React navigation completion](../../reference/api/pages.md#react-navigation-completion). -Vue, Svelte, and Solid submissions return a promise instead; use `await form.post()` to run work after a submission. +Vue, Svelte, Solid, and Hono submissions return a promise instead; use `await form.post()` to run work after a submission. ### Scroll after submission @@ -216,7 +238,7 @@ await action('/users/:id?update', { }); ``` -In React, set `preserveScroll` in the third argument to `useForm()`. In Vue, Svelte, and Solid, pass it to the submit method, such as `form.post({ preserveScroll: false })`. +In React, set `preserveScroll` in the third argument to `useForm()`. In Vue, Svelte, Solid, and Hono, pass it to the submit method, such as `form.post({ preserveScroll: false })`. ## Named Actions @@ -277,7 +299,7 @@ Each named action has its own validator schema, which supplies types for its URL Use `action()` for a button or other mutation that does not need form state. It updates the page after a successful request: ```ts -import { action } from '@void/react'; // or "@void/vue", "@void/svelte", "@void/solid" +import { action } from '@void/react'; // or "@void/vue", "@void/svelte", "@void/solid", "@void/hono" const result = await action('/users/:id?delete', { params: { id: '42' }, @@ -303,7 +325,7 @@ Authentication errors (`401`, `403`), server errors (`500`, `502`), and unexpect Use `isVoidActionError()` to tell a failed action apart from other errors. It's `true` for a `VoidActionError` at any status, and narrows the error so you can read `status` and `message`. `isCallSiteActionError()` is `true` only for the expected errors that `action()` returns. ```ts -import { isVoidActionError } from '@void/react'; // or "@void/vue", "@void/svelte", "@void/solid" +import { isVoidActionError } from '@void/react'; // or "@void/vue", "@void/svelte", "@void/solid", "@void/hono" function describeError(error: unknown) { if (isVoidActionError(error)) { @@ -410,6 +432,23 @@ export default function Upload() { } ``` +```tsx [Hono] +import { Form, useForm } from '@void/hono'; + +export default function Upload() { + const form = useForm('/photos', { title: '', photo: null as File | null }); + const input = (e: Event) => e.target as HTMLInputElement; + + return ( +
+ form.setData('title', input(e).value)} /> + form.setData('photo', input(e).files?.[0] ?? null)} /> + +
+ ); +} +``` + ::: On the server, use `c.req.parseBody()` to access the uploaded file: diff --git a/docs/guide/pages-routing/islands.md b/docs/guide/pages-routing/islands.md index abb8a166f..dd40a8acb 100644 --- a/docs/guide/pages-routing/islands.md +++ b/docs/guide/pages-routing/islands.md @@ -152,6 +152,27 @@ export default function BlogIndex(props) { } ``` +```tsx [Hono] +// pages/blog/index.island.tsx +import Counter from './_Counter' with { island: 'load' }; +import PostForm from './_PostForm' with { island: 'visible' }; + +export default function BlogIndex(props) { + return ( +
+

Blog

+ + {props.posts.map((post) => ( + + {post.title} + + ))} + +
+ ); +} +``` + ::: Components imported _without_ the `island` attribute are rendered as static HTML only. No JavaScript is sent to the browser for them. @@ -342,6 +363,41 @@ export default function PostForm() { } ``` +```tsx [Hono] +import { useIslandForm } from '@void/hono'; + +export default function PostForm() { + const form = useIslandForm({ title: '', body: '' }); + + return ( +
{ + e.preventDefault(); + return form.post('/blog'); + }} + > + + form.setData('title', (e.target as HTMLInputElement).value)} + /> + {form.errors.title && {form.errors.title}} + + +