Overview

hono-preact

Fullstack made easy.

Every route is a file pair: a .tsx page component and a .server.ts file that never reaches the browser. The framework handles SSR, data fetching, mutations, and access control — you write the logic.

// movies.server.ts — runs only on the server
const serverLoader = async () => ({
  movies: await getMovies(),
});
export default serverLoader;

// movies.tsx — the page component
export default getLoaderData(Movies, { serverLoader });

On the first request, the server runs the loader directly and preloads the result into the HTML. On hydration, the client reads from that preloaded data. On client-side navigation, the framework calls the loader over RPC. Same loader, three environments, zero config.

→ Quick Start — build a page with data and a mutation in ~10 minutes

The server/client boundary

Two Vite plugins enforce the boundary between server and client code. During the client bundle build, serverOnlyPlugin rewrites *.server.* imports: the default export becomes an RPC function, any imported serverGuards or actionGuards become empty arrays, and serverActions becomes a Proxy. Database clients, secrets, and internal API URLs in .server.* files never reach the browser bundle.

serverLoaderValidationPlugin enforces the contract at build time: .server.* files may only export serverGuards, serverActions, or actionGuards as named exports. Any other named export fails the build.