A minimal, fast, dependency-light web stack.
Zero React on the client. Server-rendered TSX. Under 25KB shipped to the browser.
| Layer | Tool | What it does |
|---|---|---|
| Runtime | Bun | Server, SQLite, TSX transpilation |
| Server | better-call | Typed endpoints, router, Zod validation |
| Auth | better-auth | Email/password, sessions, cookie-based |
| Templates | @kitajs/html | JSX → HTML strings (server-only, zero runtime) |
| Interactivity | HTMX | HTML fragment swapping (~14KB) |
| Styling | Oat.ink | Semantic CSS components (~8KB) |
# Install dependencies
bun install
# Run dev server with hot reload
bun dev
# Run production
bun startThen open http://localhost:3000
oat-stack/
├── src/
│ ├── index.tsx # Entry point — Bun.serve()
│ ├── router.ts # Imports all endpoints into createRouter()
│ ├── auth.ts # better-auth config
│ ├── db.ts # SQLite schema + queries
│ ├── routes/
│ │ ├── pages.tsx # Full page endpoints (HTML)
│ │ └── api.tsx # API endpoints (HTML fragments for HTMX)
│ ├── components/
│ │ ├── Layout.tsx # Base HTML shell
│ │ └── ui.tsx # Reusable UI components
│ └── middleware/
│ └── auth.ts # Auth middleware for endpoints
├── public/
│ └── styles.css # Minimal custom styles
├── package.json
└── tsconfig.json
- Pages return full HTML (Layout + content) for initial page loads
- HTMX makes requests to API endpoints that return HTML fragments
- HTMX swaps the fragments into the DOM — no client-side rendering
- better-auth handles sessions via cookies — works naturally with server-rendered HTML
- @kitajs/html lets you write TSX that compiles to HTML strings — full TypeScript DX, zero client JS
- Create your endpoint in
src/routes/:
import Html from "@kitajs/html";
import { createEndpoint } from "better-call";
export const myPage = createEndpoint("/my-page", {
method: "GET",
}, async (ctx) => {
return new Response(
(<Layout title="My Page"><h1>Hello!</h1></Layout>) as string,
{ headers: { "Content-Type": "text/html; charset=utf-8" } }
);
});- Import it in
src/router.ts:
import { myPage } from "./routes/pages";
// add to createRouter({ ..., myPage })That's it. No file-based routing magic, no build step, no config.
- Auth forms: The login/signup forms POST directly to better-auth endpoints. better-auth handles validation, password hashing, session creation.
- HTMX + Auth: The
authMiddlewarechecks cookies on every request. HTMX requests automatically include cookies, so auth "just works". - Database: SQLite via
bun:sqlite. The DB file isoat-stack.dbin the project root. better-auth creates its own tables alongside yours. - No bundler: Bun transpiles TSX natively. No webpack, no vite, no esbuild config.