Client-side TypeScript framework · Lit · MobX 7
A static core.
Flux everywhere else.
StaticFlux is a small, stable core of contracts — tokens, units, scopes, results, state machines, telemetry — and an ecosystem of building blocks that plug into it. Each package works on its own. Together they make large single-page apps fast, observable and boring to change.
- Core boot surface
- ≤ 7.5 KB
- Packages
- 34 + 4 tools
- Boot JS budget
- ≤ 150 KB
Declare, don’t wire
Every piece says what it needs. The injector does the rest.
A unit declares requires and provides with typed tokens. Boot resolves critical units in dependency order and traces each one into a boot waterfall; everything else stays lazy until something asks for it. A missing provider is a type error at the composition site, not a 2 a.m. page.
import { type Result, token, unit } from '@staticflux/core';
import { bootApp } from '@staticflux/runtime';
import { DataClientToken, dataClientUnit, epochSourceUnit, mockTransportUnit, pauseControlUnit } from '@staticflux/data';
import { DocsList, type DocsPage } from './endpoints.js';
export interface DocsApi { list(signal: AbortSignal): Promise<Result<DocsPage>> }
export const DocsApiToken = token('docs/api').type<DocsApi>();
export const docsApiUnit = unit({
id: 'docs/api-unit',
provides: DocsApiToken,
scope: 'app',
deps: { required: { client: DataClientToken } },
create: ({ client }): DocsApi => ({
list: (signal) => client.call(DocsList, { cursor: undefined }, { signal }),
}),
});
const booted = await bootApp({
units: [
docsApiUnit, dataClientUnit, epochSourceUnit, pauseControlUnit,
mockTransportUnit((mock) => mock.route(DocsList, () => ({ items: [], nextCursor: null }))),
],
});
The fabric
Five ideas, applied to everything.
The same five ideas shape the router, the session, the surface manager and your features alike. Learn them once; every package reads the same way.
- 01
Declarations
Tokens, flags, metrics, routes, capabilities and errors are typed values — never magic strings. Misconfiguration is a compile-time or CI error.
token('docs/api').type<DocsApi>() - 02
A requires / provides graph
Every unit declares what it needs and what it offers. The injector resolves in topological order, times every resolution and refuses cycles and scope leaks.
deps: { required, lazy, functional } - 03
Scopes & lifecycles
Every lifecycle is a typed state machine; every resource lives in a disposal scope with an AbortSignal. Leaving a state cleans up — always.
scope.listen(window, "resize", fit) - 04
Plugins, not branches
Experiments, modes and hosts are alternate bindings and separate features. Feature code never asks “which variant am I?”
binder.variants({ flag, of, variants }) - 05
One telemetry fabric
DI, navigation stages, chunks, requests, gate decisions, workers and renders all emit OpenTelemetry-shaped spans. Devtools are just a consumer.
metric.histogram('docs/render', { unit: 'ms' })
Building blocks
Verticals that work alone — and best together.
Opting in is one line and immediately pays off: register perfUnits and you get journey traces; register gateUnits and every route, slot and capability can be gated with explanations.
Navigation & URL state
A 13-stage async pipeline with exit guards, typed routes, per-mode variants, view sub-routes, routable dialogs and consume-once intents.
routerurlstatehandoffSessions, gating & credits
Pluggable identity providers, single-flight refresh, multi-tab sync; facts → policies → O(1) explainable decisions; reserve → commit meters.
authgateconfigData & storage
Transport-agnostic resources with SWR and epochs, one retry layer, breakers, schema-first stores with patches, one async storage interface.
datastorageThreads, documents & Wasm
Supervised workers, read-only replicas with intents, a CRDT-backed document engine, Wasm as worker-scoped units, multi-tab coordination.
workerdocumentwasmRich surfaces
DOM, Canvas2D, WebGL, WebGPU and HTML-in-Canvas as projections, rendered in a worker via OffscreenCanvas with forwarded input.
surfaceEditor ↔ render pipeline
Sequenced deltas with gap → resnapshot, a lossless input ring, view-state frames and brokered peer channels; one worker or two is a boot setting. A tiled GPU raster port and EditContext text input on top.
workersurfaceLocal-first documents
One CRDT doc per page behind an engine-neutral port (Loro), a repo with one commit per gesture, a composed stage with provenance and locks, per-actor undo, a content-addressed blob store on OPFS.
documentstageblobPerformance & journeys
Journey traces from first input to feature complete, framework-measured feature marks, budgets per device tier, a conformance stream.
perfloaderdevtoolsUI, shell & messaging
Lit components that self-inject render / lazy / functional deps; slots, workspaces, themes; one surface manager for every overlay.
uishellmessagingThemes, kit & layout specs
A typed theme contract with density, contrast and reduced motion; a themed component kit; screens as validated layout specs applied as minimal animated diffs.
uikitshellAccessible & localizable
Focus management, traps, announcer and shortcut registry; typed ICU catalogs with lazy locales and RTL — base requirements, not add-ons.
a11yi18nOffline & instant boot
Service worker from warmup tiers, safe updates without reload, islands over prerendered HTML, store snapshots restored before the network.
pwahydrateloaderAI-native
Typed capabilities callable by UI, palette and agents through one gated, confirmed, metered pipeline; WebMCP publishing; advisors that only tune performance.
capabilitieswebmcpaiadvisorsIsolation for untrusted code
Third-party plugins run in sandboxed iframes or workers and can only invoke the capabilities granted to them.
sandboxTooling that enforces
Vite and Rspack plugins for logical chunks and budgets, the sf CLI for graph, lint, error catalog and ratchets, and a testkit with fake time.
vite-pluginsftestkit
One stop for the modern web
Use the newest platform APIs. Ship to every browser.
StaticFlux packages feature-detect every modern API and degrade deliberately — Safari and Firefox gaps included. You write the feature once; the platform facts pick the path. Experimental APIs live in adapter packages, so the core never churns when a spec does.
- Client-only: no SSR server to run, backends behind typed contracts
- Everything lazy by default, eager only with a declared reason
- Budgets for bytes, boot time, feature marks — enforced in CI
- Accessible, localizable and testable by default
- Navigation APIHistory API fallback; memory history for embedded hosts
- scheduler.yield / postTaskMessageChannel + setTimeout shim (Safari)
- View Transitionsfor route, view and workspace changes; skipped on reduced motion
- Speculation Rulesprogressive enhancement over modulepreload and SW warmup
- SharedWorker + Web Locksmulti-tab leader election; BroadcastChannel fallback
- OffscreenCanvas · WebGPUrender workers; main-thread fallback with the same engine
- HTML-in-Canvasfeature-detected adapter; positioned DOM overlay fallback
- Wasm SIMD · threadsvariant builds picked from feature facts and isolation
- COOP / COEP isolationper-app choice; shared vs transfer channels picked for you
- Built-in AIone model-provider port; availability is a reactive fact
- WebMCPcapabilities published as agent tools, gated as subject “agent”
- IndexedDB · OPFSone async AsyncStore; localStorage and memory fallbacks
- SharedArrayBuffer + Atomicspipes between workers when isolated; ordered postMessage mirrors otherwise
- EditContexttext input port; hidden-textarea fallback chosen by a variant binding
Agent-first codebase
Explicit enough for humans. Explicit enough for agents.
Every package ships an AGENTS.md with its purpose, public API by file, rules and budgets. Every error has a stable code and a fix, collected in a machine-readable catalog. Boundaries, budgets and lint rules are enforced by tooling — so nobody, human or AI, has to guess.
{
"code": "SF-DI-002",
"kind": "invariant",
"summary": "A longer-lived unit requires a shorter-lived unit.",
"fix": "Move the dependency to a lazy dep, or lower the consumer scope."
}
Start with one unit. Grow into the whole fabric.
Clone the monorepo, run the checks, boot the Studio example — an AI-assisted document editor composing every vertical against mock services.