Skip to content

Foldkit

Neon.Website.Foldkit supports browser-only SPAs, server-side rendering (SSR), static site generation (SSG), and hybrid rendering. It packages Foldkit’s assets and optional generated handler as a Node 24 Fetch module in a Neon Function ZIP.

Use an existing Foldkit app or the runnable Foldkit example. Configure deployment credentials and install Alchemy and its matching Effect dependencies. Use Node 24 for this build toolchain, then install the build integration in the app directory:

Terminal window
pnpm add -D https://pkg.alchemy.run/@alchemy.run/frontend-frameworks/pr:2930:a10573e @vercel/nft

Keep the foldkit() plugin from @foldkit/vite-plugin in vite.config.* and retain your application’s normal entry configuration. No application adapter or server wrapper is required.

Configure rendering in the application’s foldkit(...) plugin. This example uses Foldkit 0.167.0, @foldkit/vite-plugin 0.27.0 and compatible Effect dependencies:

vite.config.ts
import { foldkit } from "@foldkit/vite-plugin";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
foldkit({
ssr: {
serverEntry: "/src/entry.server.ts",
clientEntry: "/src/entry.ts",
build: true,
},
}),
],
});

The server entry exports renderPage and renderDocument. Import stylesheets from the client entry and use Runtime.hydrate to start the browser application. Remove the source index.html for this build: Foldkit generates the complete HTML document, including the client script, stylesheets and module preload links.

src/entry.server.ts
import { Server } from "foldkit/experimental";
export const renderDocument = Server.renderDocument;

Keep your application’s renderPage export alongside the document renderer. Foldkit currently exposes its server rendering API through foldkit/experimental; see its server rendering guide for the application and hydration contract.

For static site generation (SSG), change build to { prerender: true } and export the URLs to generate from the server entry:

src/entry.server.ts
export const prerenderPaths = ["/", "/about"];

SSG renders those URLs during the build. SSR renders a page when its request arrives. A hybrid application prerenders some URLs and renders others on request. The same renderPage and renderDocument exports serve both delivery policies.

The same Website declaration deploys the client assets and generated handler. GET and HEAD requests serve matching assets and prerendered pages first; remaining requests go to the handler. Other methods bypass static assets. The application owns its response status and headers through renderPage. For an SSG-only application, return 404 for URLs outside its generated route set; allowing those URLs to render on request makes the deployment hybrid.

Browser-only builds use SPA fallback. Server builds send unmatched requests to the handler. Explicit assets.notFoundHandling settings override these defaults.

Vite’s resolved base also mounts the client assets. With base: "/app/", /app/assets/main.js serves assets/main.js from the client output. SPA fallback is limited to that prefix; server handlers receive the original URL.

alchemy.run.ts
import * as Alchemy from "alchemy";
import * as Neon from "@alchemy.run/neon";
import * as Effect from "effect/Effect";
export default Alchemy.Stack(
"NeonFoldkit",
{ providers: Neon.providers(), state: Alchemy.localState() },
Effect.gen(function* () {
const site = yield* Neon.Website.Foldkit("Web", {
rootDir: ".",
});
return { url: site.url };
}),
);

Browser-only builds serve index.html for unmatched GET/HEAD paths so the client router can resolve deep links. For a multi-page build with a 404.html, set assets.notFoundHandling to "404-page". vite.outDir overrides the build output directory.

env values with Vite’s VITE_* prefix are embedded into the browser build. Server-rendered apps read private runtime values from process.env. Keep secrets out of VITE_* keys and serialized Foldkit Flags because both reach the browser.

Terminal window
pnpm exec alchemy dev
pnpm exec alchemy deploy
pnpm exec alchemy destroy

Run separately, destroying when finished. Dev uses Vite’s native server with Foldkit live reload, returns a local URL, and creates no implicit Neon resources. Append .pipe(Alchemy.remote()) to the Website Effect to use live deployment during dev.

Live deploy creates an Ohio Project if no scope is supplied. Pass either project or branch for an existing backend; choose Ohio for combined storage use. Read scope and credentials: account keys and privileged branch tokens must not reach this browser bundle.