SDKFrameworks
SDK

Frameworks

Nuxt, SolidStart, TanStack Start, SvelteKit, Astro, React Router and Next.js.

Each framework's adapter builds the app as it would for any server, then deploys it as the SDK says: pages rendered on request, the prerendered ones and the static files as public files, and API routes. The bindings reach your code where each framework keeps a platform's.

Nitro

Nuxt, SolidStart and TanStack Start build their servers with Nitro. zeroko() from @zeroko-io/nitro is Nitro's options for Zeroko, for Nitro 2 and 3.

sh
npm install -D @zeroko-io/nitro
ts
// nuxt.config.ts
import { zeroko } from "@zeroko-io/nitro"

export default defineNuxtConfig({ nitro: zeroko() })
ts
// vite.config.ts, SolidStart (TanStack Start alike, with its plugin)
import { solidStart } from "@solidjs/start/config"
import { zeroko } from "@zeroko-io/nitro"
import { nitro } from "nitro/vite"
import { defineConfig } from "vite"

export default defineConfig({ plugins: [solidStart(), nitro(zeroko())] })

The bindings: with Nitro 2 (Nuxt), in event.context.cloudflare.env; with Nitro 3, in the request's runtime.cloudflare.env.

ts
// server/api/notes.get.ts, in Nuxt
import type { Database } from "@zeroko-io/core"

export default defineEventHandler(async (event) => {
  const { DB } = event.context.cloudflare.env as { DB: Database }
  return DB.query("SELECT * FROM notes")
})

SvelteKit

sh
npm install -D @zeroko-io/sveltekit
js
// vite.config.js
import { sveltekit } from "@sveltejs/kit/vite"
import zeroko from "@zeroko-io/sveltekit"
import { defineConfig } from "vite"

export default defineConfig({ plugins: [sveltekit({ adapter: zeroko() })] })

The bindings are in event.platform.env, in load functions, actions and endpoints. Declare them in src/app.d.ts:

ts
import type { Database } from "@zeroko-io/core"

declare global {
  namespace App {
    interface Platform {
      env: { DB: Database }
    }
  }
}

Astro

sh
npm install -D @zeroko-io/astro
js
// astro.config.mjs
import zeroko from "@zeroko-io/astro"
import { defineConfig } from "astro/config"

export default defineConfig({ output: "server", adapter: zeroko() })

The bindings are in Astro.locals.runtime.env, and in an endpoint's locals.runtime.env. Pages with export const prerender = true, and a whole static site, are public files. Images go as they are: Sharp doesn't run in a worker.

React Router

sh
npm install -D @zeroko-io/react-router
ts
// react-router.config.ts
import type { Config } from "@react-router/dev/config"
import zeroko from "@zeroko-io/react-router"

export default { ssr: true, buildEnd: zeroko() } satisfies Config

The bindings are in the router's context, in loaders and actions:

ts
import { zeroko } from "@zeroko-io/react-router/context"

export async function loader({ context }: LoaderFunctionArgs) {
  const { env } = context.get(zeroko)
  return env.DB.query("SELECT * FROM notes")
}

Next.js

Through OpenNext, which builds Next.js for workers, the runtime Zeroko runs on. @zeroko-io/next runs it after Next's own build, with its settings of its own: no wrangler.jsonc or open-next.config.ts in your project.

sh
npm install -D @zeroko-io/next @opennextjs/cloudflare wrangler
js
// next.config.mjs
import { withZeroko } from "@zeroko-io/next"

export default withZeroko({})
js
// zeroko.build.js, run after Next's build:
// "build": "next build && node zeroko.build.js"
import { build } from "@zeroko-io/next"

await build()

The bindings are in OpenNext's context, in server components, route handlers and server actions:

ts
import { getCloudflareContext } from "@opennextjs/cloudflare"

export async function GET() {
  const { env } = await getCloudflareContext({ async: true })
  return Response.json(await env.DB.query("SELECT * FROM notes"))
}

What runs on workers runs here: OpenNext's limits apply, such as no Node-only image optimization.