Reference

Platforms

Web, server, desktop and mobile platforms: options, commands and layout.

Where an app runs is a platform: a named entry in kitrrc.ts built by a factory from a platform package. The core knows only the contract (definePlatform in @kitr/core); each package owns its options, its scaffold, its dev server and its build.

// kitrrc.ts
import { defineKitrConfig } from 'kitr/app'
import { web } from 'kitr/web'
import { server } from 'kitr/server'
import { desktop } from 'kitr/desktop'

export default defineKitrConfig({
  platforms: {
    site: web({ mode: 'ssr', pwa: true }),
    api: server({ runtime: 'node', port: 3001 }),
    app: desktop({ shell: 'tauri', title: 'My App' }),
  },
  packages: [...],
  providers: [...],
})

Names are yours. One project can hold several platforms of the same type (admin: web({...}), www: web({...})), and every command addresses them by name.

Type Package Factory What it builds
web @kitr/web web() from kitr/web A browser app through TanStack Start: static (prerendered pages), spa, or ssr. pwa: true adds the manifest and service worker.
server @kitr/server server() from kitr/server The h3 server in server/main.ts on Node, Bun, Deno or Cloudflare Workers.
desktop @kitr/desktop desktop() from kitr/desktop A Tauri or Electron shell around the SPA build. Native project in platforms/<name>/.
mobile @kitr/mobile mobile() from kitr/mobile A Capacitor shell for iOS and Android. Native project in platforms/<name>/.

Commands

Command What it does
kitr dev [names...] Starts every platform (or the named ones) in parallel: Vite for web, tsx watch / bun --watch / wrangler dev for server, the Tauri or Electron dev window for desktop, Capacitor live reload for mobile.
kitr build [names...] Production builds, in order. Web platforms land in dist/<name>/ (client/ and, for SSR, server/server.js).
kitr start [names...] Serves the builds: srvx for SSR and static sites, the compiled server for server.
kitr add <type> <name> [--option value] Adds a platform: installs the package, writes platforms.<name> into kitrrc.ts, adds the packages the platform needs (router for web, router platform storage for desktop and mobile, server for server), scaffolds its files and the dev/build/start scripts. kitr add web admin --mode spa --port 3002.
kitr platforms Lists the platforms and their effective options. kitr platforms types lists the available types, kitr platforms scaffold <name> rewrites a platform's files, kitr platforms remove <name> takes it out of kitrrc.ts.
kitr config <name> [option] [value] Reads or sets one option in kitrrc.ts, validated against the platform's option schema: kitr config site port 4000, kitr config api runtime bun, kitr config site pwa --unset.

Every option can be overridden at run time without touching the file: KITR_SITE_PORT=4000 kitr dev site, or kitr dev site --port 4000. The name is upper-cased with non-alphanumerics turned into _.

kitr doctor runs each platform's own checks (the Rust toolchain for Tauri, Xcode and Android Studio for Capacitor, wrangler for Workers).

Options

web (@kitr/web)

Option Default Description
mode spa static: prerender every route to HTML. spa: one shell, client routing. ssr: render on request through TanStack Start.
port 3000 Dev and production port.
host Bind address (localhost in dev, 0.0.0.0 in production).
pwa false true or { name, shortName, themeColor, ... }: web manifest, service worker (vite-plugin-pwa, autoUpdate), useInstallPrompt, useServiceWorker from kitr/web/client.
base / Public base path.
open false Open the browser on kitr dev.

Prerendering, the sitemap and robots.txt come from config/seo.ts (kitr add seo).

server (@kitr/server)

Option Default Description
runtime node node, bun, deno or workers (Cloudflare, via wrangler.toml).
port 3001
host
entry server/main.ts createServer({...}) plus routes and realtime. A React project with a web platform also gets app/routes/api.$.ts, which mounts the same server inside TanStack Start.

desktop (@kitr/desktop)

Option Default Description
shell tauri tauri (Rust, small binary, platforms/<name>/src-tauri) or electron (electron-vite, platforms/<name>/{main,preload,ipc}.ts).
port 3200 Dev server the window loads.
title, width, height, minWidth, minHeight Window.
appId Bundle identifier.
devtools true Open devtools in dev.

kitr/desktop/client tells which shell is running (isTauri, isElectron, desktopShell); kitr/desktop/tauri has defineCommands, commandQuery, commandMutation and useTauriEvent, kitr/desktop/electron the typed IPC bridge with kitr/desktop/electron-main and kitr/desktop/electron-preload for the other two processes. kitr/desktop/provider registers the platform and storage drivers for the shell in use.

mobile (@kitr/mobile)

Option Default Description
targets ios,android Native projects; kitr dev/kitr build run cap add <target> the first time and `--target ios
appId, appName Bundle identifier and display name.
scheme Custom URL scheme for deep links.
port 3100 Dev server for live reload on the device.

kitr/mobile/provider registers the Capacitor drivers; kitr/platform/react (usePlatform, useAppState, useBackButton, useDeepLink, useSafeArea) and kitr/storage then answer the same way in the browser and in the shell.

Layout

app/                 # routes, components, features, channels: shared by every UI platform
server/
  main.ts            # the server platform entry (createServer + routes + realtime)
  routes.ts          # defineRoutes(...)
  controllers/
  realtime.ts        # createRealtimeServer({ channels })
config/              # one file per package (config/seo.ts, config/realtime.ts, config/theme.ts, ...)
platforms/
  app/               # desktop: src-tauri/ or the Electron main/preload sources
  phone/             # mobile: capacitor.config.ts, ios/, android/
dist/<name>/         # build output per platform
kitrrc.ts

Writing a platform package

import { definePlatform } from 'kitr/app'

export const kiosk = definePlatform<{ port?: number }>(
  {
    type: 'kiosk',
    description: 'Fullscreen Chromium on a Raspberry Pi',
    describe: { port: { type: 'number', default: 3300, description: 'Dev server port' } },
    requires: ['router', 'platform'],       // recipes `kitr add kiosk <name>` runs first
    provider: '@acme/kitr-kiosk/provider',  // registered in kitrrc.ts providers
  },
  (options) => ({
    dependencies: { chromium: '^1.0.0' },
    async scaffold(ctx) { await ctx.write('kiosk.json', '{}') },
    async dev(ctx) { await ctx.exec('vite', ['--port', String(options.port)]) },
    async build(ctx) { await ctx.exec('vite', ['build', '--outDir', ctx.dir('dist')]) },
    async start(ctx) { await ctx.exec('chromium', ['--kiosk', `http://localhost:${options.port}`]) },
    async doctor(ctx) { return [] },
    agents: 'Run `kitr dev kiosk` and open the window on the Pi.',
  }),
)

ctx carries the platform name, the project root, the resolved options, the package manager, the other platforms, the flags from the command line, an abort signal and log, run and exec helpers that resolve node_modules/.bin. kitr/vite reads import.meta.env.KITR_PLATFORM and KITR_PLATFORM_TYPE so app code can branch per platform at build time.