Preact Renderer
@farm.js/preact connects Preact JSX compilation, server rendering and streaming, browser
hydration, and Prefresh to the FARMJS renderer contract. React remains the default unless the
application selects Preact.
Create an app
pnpm create @farm.js/app@beta my-preact-app --template basic --renderer preact --typescriptFor an existing Basic app, install the adapter and Preact:
pnpm add @farm.js/preact@beta preactimport { defineConfig } from "@farm.js/core";
import { preact } from "@farm.js/preact";
export default defineConfig({
renderer: preact(),
});Pages and layouts
Preact routes use .tsx or .jsx. Layout children use Preact's ComponentChildren type:
import type { Metadata } from "@farm.js/core";
import type { ComponentChildren } from "preact";
import "./globals.css";
export const metadata: Metadata = {
title: "FARMJS with Preact",
};
export default function RootLayout({ children }: { children?: ComponentChildren }) {
return <>{children}</>;
}Hydrate interactive routes
Mark an interactive component with "use client", then use Preact hooks normally:
"use client";
import { useState } from "preact/hooks";
export function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount((value) => value + 1)}>Count: {count}</button>;
}FARMJS renders route HTML with preact-render-to-string, uses its Node or Web streaming runtime
when the deployment supports streaming, and calls Preact's hydrate() to claim the existing DOM.
See Preact's API reference and
server-rendering guide for runtime-specific
behavior.
Call FARMJS server code
API routes, endpoint schemas, server functions, middleware, cache, storage, and observability are renderer-neutral. Use the generated API client from Preact UI:
"use client";
import { useState } from "preact/hooks";
import { api } from "../lib/api.generated";
export function Greeting() {
const [message, setMessage] = useState("Ready");
const callServer = async () => {
const result = await api.greeting.post({ body: { name: "Preact" } });
if (result.data) setMessage(result.data.message);
};
return <button onClick={callServer}>{message}</button>;
}The endpoint can call a validated createServerFn; its handler, database access, and secrets stay
in the server bundle.
React compatibility
The renderer uses the official @preact/preset-vite, which aliases React and React DOM imports to
preact/compat. This lets many existing FARMJS client components and hooks run without shipping a
second UI runtime. Compatibility for third-party React integration providers still depends on each
provider, so verify provider UI before relying on it in production.
React Server Components remain React-only. Programmatic UI routes, Markdown/MDX visual pages, the docs adapter, generated JSX metadata images, and integration UI providers should be treated as compatibility surfaces until their Preact paths are covered by dedicated tests.
The Better Auth starter includes native Preact routes and forms:
pnpm create @farm.js/app@beta my-auth-app --template better-auth --renderer preact --typescriptOther integration starter templates currently target React, so add their renderer-neutral provider code to a native Basic starter.
Run the complete example:
pnpm --filter farm-preact-renderer-example devSee the Preact renderer example and the renderer support matrix.
Preact applications can import FARMJS client hooks from @farm.js/preact/bindings. The adapter
routes those React-shaped hooks through its existing preact/compat aliases.
Use Preact components, hooks, SSR, streaming, hydration, and typed FARMJS server calls through the @farm.js/preact adapter.