Skip to content

Installation

This page gets a Stator project onto disk and the dev loop running.

Terminal window
pnpm create stator my-app
cd my-app && pnpm install && pnpm dev

The scaffold is a complete working app — a counter machine, a .stator page, and the full script set (dev, typecheck, build, start). The rest of this page is a tour of what create-stator just wired for you — or instructions for wiring it by hand.

  • Node.js (current LTS or newer) and a package manager (npm, pnpm, or yarn).
  • TypeScript — Stator is TypeScript-first; machines and props are fully typed.
  • tsx to run the TypeScript entry points directly during development.
Terminal window
npm i @statorjs/stator
Terminal window
npm i -D tsx typescript

Stator publishes several entry points; you import from the one that matches the layer you’re in:

  • @statorjs/stator/serverdefineMachine, stores, logger.
  • @statorjs/stator/devcreateDevApp (the Vite-backed dev server).
  • @statorjs/stator/buildsyncTypes and the production build.
  • @statorjs/stator/templateread, each, when, raw, …
  • @statorjs/stator/clientStatorElement, use, machine (client islands).
  • @statorjs/stator/components — shipped components like <JsonLd>.

A Stator app is organized by convention:

my-app/
├── machines/ # defineMachine definitions (one per file)
├── routes/ # .stator pages and .ts API routes (file-based)
├── templates/ # reusable .stator components and layouts
├── static/ # served as-is
├── server.ts # boots the dev server
└── sync.ts # regenerates .stator type declarations

Files in machines/ and routes/ are auto-discovered — there is no central registry to maintain.

The compiler writes per-component type declarations (.stator.d.ts) into a framework-managed .stator/types/ directory so that tsc and your editor can type component props. Add that directory to .gitignore, and run the sync step whenever component props change.

server.ts selects a store and starts the dev server. During development, Vite compiles .stator files on the way in:

import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { InMemoryStore } from '@statorjs/stator/server'
import { createDevApp } from '@statorjs/stator/dev'
const here = dirname(fileURLToPath(import.meta.url))
const app = await createDevApp({
root: here,
machinesDir: resolve(here, 'machines'),
routesDir: resolve(here, 'routes'),
staticDir: resolve(here, 'static'),
store: new InMemoryStore(),
sessionTtlSeconds: 86_400,
})
await app.listen(Number(process.env.PORT ?? 3000))

Type declarations for .stator components are generated by syncTypes. Put this in sync.ts:

import { dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { syncTypes } from '@statorjs/stator/build'
const here = dirname(fileURLToPath(import.meta.url))
const { written } = await syncTypes(here)
console.log(`stator: wrote ${written} .stator.d.ts files`)

Wire your package.json scripts to match apps/example:

{
"type": "module",
"scripts": {
"sync": "tsx sync.ts",
"typecheck": "tsx sync.ts && tsc --noEmit",
"dev": "tsx watch server.ts"
}
}

"type": "module" is required — server.ts and sync.ts use top-level await, which only works in ES modules (without it, tsx compiles to CommonJS and errors).

Run the sync script after changing a component’s props (or wire it ahead of tsc, as in typecheck above):

Terminal window
npm run sync
  • Quick start — a machine, a route, and an event, end to end.