v0.3.0
hono-preact v0.3.0 — release notes
Draft for the gh release create v0.3.0 body. Headline features, breaking changes, and the upgrade path from v0.2.0.
hono-preact v0.3.0
Web-platform alignment: progressive-enhancement forms, cancellation-aware loaders and actions, and opt-in browser link prefetch.
Highlights
- Progressive-enhancement forms (Spec C, #59).
<Form>submissions now work without client JavaScript. The form postsmultipart/form-datato the page's own URL; the framework's page handler runs the action, then redirects, re-renders with errors, or streams. With JS on, the framework intercepts the submit and content-negotiates the same URL withAccept: application/jsonto get the result back without a full page round-trip. One endpoint, content-negotiated. useActionResultanduseFormStatushooks (Spec C, #59). Read the most recent action result on the no-JS-recovery path, and observe pending/error state of the form's submission. Both compose naturally withuseOptimistic.- Loader and action timeouts (Spec A, #56).
defineLoaderanddefineActionaccepttimeoutMs, default30_000. Timed-out calls surface a structuredTimeoutOutcome(catch withisTimeout(error)orerror instanceof TimeoutError). ComposesAbortSignal.anyover the request signal andAbortSignal.timeout. Opt out withtimeoutMs: false. useOptimisticView Transitions (Spec A, #56). Pass{ transition: true }to wrap the settle and revert paths indocument.startViewTransition. The initial mutate stays unwrapped so optimistic UI still paints in the same frame.- TransformStream-based SSE codec (Spec A, #58). Streaming loaders and actions now use
TransformStream+TextDecoderStream, replacing the hand-rolled SSE codec. Wire format is unchanged; thehono/streamingdependency is gone from this path. - Speculation Rules emitter (Spec E, #60). Set
defineApp({ speculation: true })to emit a single<script type="speculationrules">in<head>that tells supporting browsers (Chrome, Edge) to prefetch same-origin<a href>links on moderate eagerness. Opt individual links out with<a data-no-prefetch>. Off by default; non-Chromium browsers ignore the tag.
Breaking changes
Actions endpoint moved from /__actions to the page URL
The framework no longer mounts a global /__actions handler. Actions are invoked at the page's own URL via POST and dispatched by __module / __action body fields. The default <Form> markup works unchanged; the change is wire-level.
If your app mounted custom middleware on /__actions, move it to the page route or to the page-level use array. The Vite plugin's reserved-path check still guards the legacy URL: building with a route at /__actions errors out.
Action result envelope unified
The JSON response shape from action POSTs is now uniformly __outcome-tagged:
{ __outcome: 'success', data }
{ __outcome: 'redirect', to, status }
{ __outcome: 'deny', status, message, data? }
{ __outcome: 'timeout', timeoutMs }
{ __outcome: 'error', message }useAction reads __outcome first and dispatches; no consumer code changes. Apps that fetched /__actions with hand-rolled fetch() calls must update to match this shape and the new URL.
Default 30-second loader and action timeout
Any loader or action that legitimately runs longer than 30 seconds now produces TimeoutOutcome instead of completing. Opt out per-call:
defineLoader(fn, { timeoutMs: false });
defineAction(fn, { timeoutMs: false });Or set a longer deadline: { timeoutMs: 120_000 }. Streaming loaders apply the timeout to total time-to-completion of the stream, not time-to-first-chunk.
New API surface
defineApp({ speculation: true })— opt in to the Speculation Rules emitter.defineLoader(fn, { timeoutMs })/defineAction(fn, { timeoutMs })— per-call deadline override.useActionResult()— read the latest action result on the PE recovery path.useFormStatus()— observe pending state of a<Form>submission.useOptimistic(reducer, initial, { transition: true })— wrap settle/revert in View Transitions.useOptimisticAction(action, { transition: true, ...existing })— same option.pageActionHandler(serverModules)— handler for custom server entries that need to run page-URL POSTs themselves.isTimeout(value)/TimeoutError— narrow on the new outcome variant.<a data-no-prefetch>— per-link opt-out from speculation prefetch when enabled.
Docs
Three new docs pages and one updated:
/docs/link-prefetch— Speculation Rules opt-in and audit guide./docs/actions— updated for the PE-form path and the unified envelope./docs/render-page—options.appConfigdocumented for custom server entries.
Full docs: https://framework.sbesh.com/docs · Demo: https://framework.sbesh.com/demo
What's not in this release
- Navigation API as primary intercept (Spec B). Deferred. Waiting on first-class Navigation API support in
preact-isoupstream; the framework will adopt the option there once it lands. - Suspense-streamed SSR via
renderToReadableStream(Spec D). Shelved. The existing async-generator loader streaming covers the same capability for slow data, and the late-outcome and SEO trade-offs of Suspense streaming aren't worth the ergonomics gap. See the roadmap doc for the rationale.
Install
pnpm add hono-preact hono preact preact-iso preact-render-to-string hoofd
pnpm add -D vite
# Pick one adapter:
pnpm add -D @cloudflare/vite-plugin wrangler # Cloudflare Workers
pnpm add @hono/node-server # Node.js (add @hono/node-ws for WebSockets)Or scaffold a new project:
pnpm create hono-preact my-appRelease mechanics
Driven by scripts/release.mjs. Reads the version from packages/hono-preact/package.json, sanity-checks that create-hono-preact and both template pins agree, publishes hono-preact first (its prepublishOnly runs tsc and consolidate.mjs to inline the @hono-preact/iso|server|vite source into dist/), then create-hono-preact second, then tags and pushes. Idempotent: a version already on the registry is skipped, so you can re-run after a partial failure.
pnpm release:dry # preview tarball contents and tag plan, no upload
pnpm release # real publish + git tag + push tag
gh release create v0.3.0 -F docs/superpowers/specs/2026-05-24-v0.3-release-notes.md --latestThe gh release create step is intentionally separate so you can review or edit the notes before publishing.
Lockstep versioning policy: hono-preact and create-hono-preact move together on minor/major bumps; patches are independent. Template hono-preact dep pins track the minor.
Pre-flight edits already applied on this branch:
- Templates:
hono-preact: ^0.2.0→^0.3.0(both Cloudflare + Node templates). packages/hono-preact/package.jsonversion:0.2.0→0.3.0.packages/create-hono-preact/package.jsonversion:0.2.0→0.3.0.