Get started
main.rs needed.1. Scaffold
The starter template lays down:
my-app/
├── .tibi-config # TOML: server / plugins / routes / redirects / env / store / cron
├── .gitignore
├── package.json # `preinstall` curls the tibi installer; scripts wrap `tibi …`
└── pages/
├── _layout.tibi # nav header + footer
├── _404.tibi
└── index.tibi
That's it — no Cargo.toml, no src/main.rs, no Dockerfile. The tibi binary reads .tibi-config, scans pages/, and runs the server. tibi build produces a dist/ folder that drops onto every static host (Cloudflare Pages, Netlify, Vercel, GitHub Pages, S3) unchanged. The package.json is the only platform-glue we ship: its preinstall hook curls the Tibi installer when any PaaS runs npm install, so the build environment has tibi on PATH before npm run build fires — see Deploy for per-platform snippets.
2. Run
Need custom Rust code (extra tower layers, app state, custom handlers)? Drop a Cargo.toml + src/main.rs next to .tibi-config and call tibi::serve(tibi::TibiConfig::load(".tibi-config")?).await. The library API stays available for power users.
3. Add a page
Drop any .tibi file under pages/:
The file-based router picks it up at /about on next request — no registration needed.
4. Dev mode + HMR
Run via the CLI to enable the file watcher:
Edit any .tibi file — the browser receives a reload event over /__tibi/ws/hmr (WebSocket) and refreshes automatically.
5. Add a component
Drop components/Greeting.tibi at the project root (a sibling of pages/, not nested inside it):
my-app/
├── .tibi-config
├── pages/
│ └── …
└── components/
└── Greeting.tibi
Reference it from any page via <Greeting name="Alice"/>. Uppercase tag = component lookup. Each component compiles to a real Web Component (<tibi-greeting>) carrying a declarative shadow DOM, so its styles never leak and its host attributes drive its render.
6. Add interactivity
A <script> block inside the component runs once on first connect with el bound to the host element and root bound to its shadow root. Query into the component's template with root.querySelector(...). Use the global Tibi.store for IDB-backed persistence (keys follow a namespace:rest convention; declare a [[store]] entry in .tibi-config to pin the default TTL for a namespace) or Tibi.ws.subscribe(channel, fn) for server pushes (WebSocket transport).
Add a tibi attribute to the script tag (<script tibi>) to opt into the enhanced surface: $name refs from quid="name" in the template, state() / computed() / effect() reactivity, declarative template bindings ({{expr}}, :attr, t-show, t-bind, t-if / t-else-if / t-else, t-for), and onMount / onUnmount / on(...) / emit(...) lifecycle helpers. See Architecture for the full surface.
7. Compile-time data via frontmatter
Open a .tibi file with a --- fence to drop a JavaScript, Python, or TypeScript script that runs at compile time:
The runner exposes a ctx variable (file path, project's pages/ directory) and reads result back as JSON. Top-level keys land as {token} substitutions in the template. See Frontmatter for the full per-language contract.