Installation
Scaffold a project
Create a new project with the initializer. It's interactive — pick a project type, language, and (for apps and libraries) a styling solution. Use whichever package manager you prefer:
bun create @opentf/web my-appnpm create @opentf/web@latest my-apppnpm create @opentf/web my-appyarn create @opentf/web my-appThe scaffolder resolves every @opentf/* dependency to the latest published version on npm, writes the project files, then runs <pm> install with the package manager that invoked it (npm, pnpm, yarn, or bun).
| Prompt | Options |
|---|---|
| Project type | See choosing a project type below |
| Language | JavaScript (.jsx) or TypeScript (.tsx + tsconfig.json) |
| Styling | Plain CSS or Tailwind CSS (SPA, Fullstack, and Library only) |
Choosing a project type
Not sure which to pick? Each option scaffolds a different shape of project:
| Option | What it is | Pick this when… |
|---|---|---|
| SPA (browser-only) | A normal web app whose UI runs in the browser. You deploy static files (dist/) — no server code in the repo. | You want a dashboard, marketing site, or client app with no backend logic (or you call an external API). |
| Fullstack (browser + server) | Same UI plus server files: middleware, API routes, and route loaders. Includes otfw serve for per-request SSR. | You need auth, a database, or any logic that must not run in the browser. |
| Documentation site | An MDX docs or blog site with navbar, sidebar, and search — powered by @opentf/web-docs. | You're building product docs, a handbook, or a content site (like this one). |
| Library | A package of reusable components — not a runnable app. Ships with bun test. | You're publishing UI primitives or widgets for other apps to install. |
SPA = browser only, static hosting. Fullstack = browser + server files (middleware, /api/* routes, loaders). If you might add a database or secrets later, start with Fullstack — you can still deploy static output, but the server pieces are already in place.
Start developing
For SPA, Fullstack, and Documentation site projects, start the dev server:
cd my-app bun run dev
The dev server compiles each route on first visit. Saving files under app/ rebuilds affected chunks and refreshes the browser tab (full-page reload) — not module-level HMR yet. Restart the dev server after adding a new route folder or changing otfw.config.js.
For Library projects, run the included test suite instead:
cd my-library bun test
See Library template for how to publish and consume a component package.
OTF Web targets modern browsers and runs its toolchain on Bun or Node 18+.
Production build
Build a static bundle you can host anywhere (App and Docs projects):
bun run build
See Deployment for build output, hosting, and a fully pre-rendered (Static Generation) site.
Editor setup
The compiler uses a custom JSX pipeline. Point your jsconfig.json (or tsconfig.json) at it so the language server understands your components:
{ "compilerOptions": { "jsx": "preserve", "jsxImportSource": "@opentf/web" } }
TypeScript projects scaffolded with create-web already include a tsconfig.json and macro typings in app/otfw-env.d.ts.
It tells your editor where JSX types come from, so autocomplete and type-checking match what the compiler actually emits — without affecting the build.