Getting started with facett-app
facett-app is the app-framework layer over facett's egui components — a Leptos-spirit framework, but immediate-mode and canvas-only (native + wasm from one codebase; no DOM/SSR/SEO, no signals). It gives you navigation, a router, overlays and an app shell so you compose whole apps out of Facets.
Full template:
examples/starter_app.rs—cargo run -p facett-app --example starter_app.
1. Add the deps
[dependencies]
facett-core = { path = "../facett-core", version = "0.1" }
facett-app = { path = "../facett-app", version = "0.1" }
egui = "0.34"
[dev-dependencies]
eframe = { version = "0.34", default-features = false, features = ["default_fonts", "glow"] }
2. A page is just a Facet
use facett_core::{Facet, FacetCaps};
struct HelloPage { title: String }
impl Facet for HelloPage {
fn title(&self) -> &str { &self.title }
fn ui(&mut self, ui: &mut egui::Ui) { ui.heading("Hello"); }
fn state_json(&self) -> serde_json::Value { serde_json::json!({}) }
fn caps(&self) -> FacetCaps { FacetCaps::NONE.themeable() }
}
3. Describe navigation once (NavModel), mount pages in an AppShell
use facett_app::{AppShell, NavItem, NavModel};
let model = NavModel::new([
NavItem::new("home", "Home").icon("🏠"),
NavItem::new("hello", "Hello").icon("👋"),
]);
let shell = AppShell::new("My App", model)
.with_body("home", "Welcome!")
.with_pane("hello", Box::new(HelloPage { title: "hello".into() }));
AppShell draws a topbar (title + back/forward + clickable breadcrumb), a left Sidebar from the NavModel, the active route's mounted pane, and a statusbar.
4. Run it (the shell is the whole window)
impl eframe::App for App {
fn ui(&mut self, root: &mut egui::Ui, _f: &mut eframe::Frame) {
egui::CentralPanel::default().show(root, |ui| self.shell.ui(ui));
}
}
What else is in the box
| Need | Use |
| Nav model → sidebar/breadcrumb/menubar | NavModel / NavItem |
| Route → view, back/forward, deep-link | Router (navigate, to_hash, sync_from_hash) |
| Persistent nav rail | Sidebar |
| Select / combobox | Dropdown |
| Dialogs (scrim + Esc) | Modal |
| Notifications | Toasts (Info/Success/Warn/Error, TTL) |
| Collapsible sections / number / pager | Accordion / Stepper / Pagination |
| Lightweight nav+content shell | NavShell |
Deep links
Router::to_hash() gives #route; on wasm write it to window.location.hash, and restore on load with router.sync_from_hash(&location.hash()). Pure strings, so the native build can use the same routes (e.g. a CLI arg).
The contract
Every facett component (yours included) should keep observable state in the struct (not egui memory), expose it via state_json, and stay headless-renderable — so the whole app is robot-testable without a human. See .nornir/component-contract.md.