Getting Started | Koze | Primitives Docs

Getting Started

Install koze, koze/vite, and boot a first app on Cloudflare Workers

Install

Koze is Vite-first. Every new project runs through vite dev / vite build with the Koze plugin.

Scaffold with koze

npx koze create my-app
cd my-app
bun install
bun run dev

The scaffolder drops a working Vite project with routes, assets, and a wrangler.jsonc pointed at src/worker.ts.

Or install into an existing project

npm install @kuratchi/koze
npm install -D vite wrangler @cloudflare/workers-types

Optional packages (install only what you need):

Package When
kunii D1 / Durable Object SQLite schema + query builder
kyzen Credentials, sessions, OAuth, RBAC, Turnstile
kuzan Ready-made HTML component library
tailwindcss @tailwindcss/vite Tailwind CSS (plain Vite plugin)
lightningcss CSS transformer (configure via vite.config.ts)

Minimal project shape

my-app/
├─ package.json
├─ vite.config.ts
├─ wrangler.jsonc
├─ tsconfig.json
└─ src/
   ├─ worker.ts                    export { default } from 'koze:worker'
   ├─ middleware.ts                defineMiddleware({ ... })
   ├─ app.koze                 document shell
   ├─ app.css                      global stylesheet (optional)
   ├─ assets/                      static files served verbatim at /
   ├─ server/                      auto-discovered DOs / workflows / queues / pipelines / containers
   └─ routes/
      ├─ layout.koze           shared fragment
      └─ index.koze            /

There is no project-level config file. Build-time options (security headers, directory overrides) are passed to the koze() Vite plugin. Request-time concerns (auth, ORM auto-migration, custom steps) live in src/middleware.ts.

For a full reference of every special file, see Project structure.

vite.config.ts

The Koze plugin owns routing, virtual modules, and Wrangler sync. Nothing else to configure for the common case.

import { defineConfig } from 'vite';
import { koze } from '@kuratchi/koze/vite';

export default defineConfig({
  plugins: [koze()],
});

wrangler.jsonc

{
  "name": "my-app",
  "main": "src/worker.ts",
  "compatibility_date": "2026-01-01",
  "compatibility_flags": ["nodejs_compat"],
  "assets": { "directory": "src/assets" }
}

The Koze plugin auto-maintains durable_objects, containers, queues.consumers, pipelines, and migrations[] entries based on what it discovers in src/server/. Do not hand-edit those fields.

package.json scripts

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "deploy": "vite build && wrangler deploy"
  }
}

src/worker.ts

export { default } from 'koze:worker';

That's the whole file. The virtual module synthesizes fetch + queue handlers, Durable Object exports, and any discovered convention classes.

src/app.koze — document shell

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>My App</title>
</head>
<body>
  <slot></slot>
</body>
</html>

Optional — the framework provides a minimal default shell when absent. Don't add a <link> for your global CSS; Koze injects it (see next section).

src/routes/layout.koze — shared fragment

Wraps every page. Not a document — no <!DOCTYPE>, no <html>.

<script>
  import { url } from 'koze:request';
  const current = url.pathname;
</script>

<header>
  <a href="/" class={current === '/' ? 'active' : ''}>Home</a>
  <a href="/about" class={current === '/about' ? 'active' : ''}>About</a>
</header>

<main>
  <slot></slot>
</main>

src/routes/index.koze — first page

<script>
  const items = ['Alpha', 'Bravo', 'Charlie'];
</script>

<h1>Hello Koze</h1>

if (items.length > 0) {
  <ul>
    for (const item of items) {
      <li>{item}</li>
    }
  </ul>
} else {
  <p>No items yet.</p>
}

The page is server-rendered. The top <script> runs during SSR; $server/* imports become RPC stubs when the same code runs in the browser.

First stylesheet — src/app.css

Drop a file at src/app.css. Koze registers it as a Vite client entry, runs every CSS plugin on it (Tailwind, PostCSS, CSS Modules, Lightning CSS), and auto-injects <link rel="stylesheet"> into your app shell before </head>. You don't write the <link> tag.

/* src/app.css */
body {
  margin: 0;
  font-family: system-ui, sans-serif;
}

That's the full setup. Add Tailwind by installing @tailwindcss/vite, dropping it into vite.config.ts, and @import "tailwindcss" from src/app.css. See Styling for Tailwind, CSS frameworks, and static asset conventions.

First browser-side JavaScript

Koze routes have one script block. Declare a handler in the top <script> and bind it with onclick={fn(args)}:

<script>
  import { deleteItem } from '$server/items';

  async function confirmDelete(id) {
    if (!confirm('Delete?')) return;
    await deleteItem(id);
    location.reload();
  }
</script>

<ul>
  for (const item of items) {
    <li>
      {item.title}
      <button type="button">Delete</button>
    </li>
  }
</ul>

The same script runs on the server (resolving the await) and in the browser (wiring up confirmDelete). No addEventListener, no document.querySelector, no typeof document guards.

See Client interactivity for the event-handler reference, SSR data hydration, and browser-only DOM patterns inside the single route script.

Run locally

bun install
bun run dev

vite dev serves the app on http://localhost:5173 with the Cloudflare Vite plugin providing a Workers-accurate runtime.