Reference

Design decisions

The rules behind kitr's architecture and what each one means for your code.

The rules behind kitr's architecture, and what each means for your code.

Re-export, never re-implement

A @kitr/* package hands you the upstream library unchanged and adds a thin layer on top. The libraries' own docs keep applying, and leaving kitr means rewriting imports. In your code, import from @kitr/<name> and read the upstream docs for everything kitr does not mention.

Everything the app loads is listed in one file

kitrrc.ts names the packages, providers and platforms, and config/<name>.ts holds each package's options. In your code, read those two places to know what runs at startup, and edit them by hand if you like.

Platforms are packages

Where an app runs is a platform package (@kitr/web, @kitr/server, @kitr/desktop, @kitr/mobile) built on one contract. Because the platform is a package, a project starts with only the core and the CLI, and kitr dev, kitr build and kitr start treat every platform the same way. In your code, add the platforms you need and name them in kitrrc.ts.

Pages, endpoints and sockets are entries on one route table instead of files in folders. One table shape serves the browser and the server, and kitr add can edit the list with a codemod. In your code, add a page with .page() in app/routes.ts; links, loaders and search params are TanStack Router's.

Typed from the server routes, no codegen

The API client gets its types from your route table and controllers through the compiler. In your code, change a route and the client's types change with it.

Queries and mutations are named contracts

Every data source describes its reads, writes and live updates in the same shape, and a mutation declares which cached data it makes stale. Components never know which source they use, so you can swap a REST endpoint for a server action without touching them.

State lives on the app, not in modules

There is one app per browser tab, per server process and per test, plus a fork of the app for each server request. Request data lives on that request's fork, so one visitor's data cannot reach another's page. In your code, register services in a provider and read them with useService(), never from a module-level variable.

A native shell without a driver fails loudly

In Electron, Tauri or Capacitor, a device package with no driver throws E_DRIVER_UNAVAILABLE instead of quietly using the web version. Web code must never pass for native, for example secrets kept in WebView storage. In your code, opt in to the web driver with fallbackToWeb: true when you want it.

Packages ship their own setup

Each package carries its own kitr add steps, generators and docs. Adding or removing a package therefore changes your project in a known, listed way. In your code, run kitr add instead of wiring by hand, and read what it changed under each package's Install section.

The CLI is for agents too

Every command runs from flags, prints plain output and supports --dry-run. Coding agents and CI can run anything a person can. In your code, expect no prompts; every question has a flag.

Next steps