Local-first data
The UI reads from durable local storage instead of waiting on the network. Storage failures are surfaced, never silently swapped for memory.
local-first meta-frameworkv0.9.1 · alpha · MIT
Installable web apps where the network is optional and the user holds the keys.
$ deno run -A jsr:@nzip/lofi/create my-appRequires Deno 2.9+. Astro + Preact + Jazz 2, generated.

Every user gets an on-device account at first open, even when the app has no sync service configured at all. Nothing to sign up or waiting.
The UI reads durable local data instead of waiting on a round trip. When storage fails, lofi surfaces it rather than quietly falling back to memory and risking the data.
Connect managed Jazz sync and a user can back up the account they already have — recovering it with a passkey where supported, or a 24-word phrase. Data made offline comes along, and a fresh device can connect and recover in either order.
deno task jazz:provision
or scaffold with --sync from the start.
at the core of the idea
This is the starter task app the generator gives you. Turn the network off, keep typing on both devices, then turn it back on. Edits from both sides survive and every replica converges on the same result: same-field collisions resolve last-writer-wins, everything else merges cleanly. That is what the CRDT buys you. A reworked build of this starter runs installable at demo.lofi.host, deployed from the latest release.
This phone
Your other device
A simulation, drawn to the same model (not a live Jazz session). For the real one, and for the part where you close the laptop and it is all still there tomorrow: deno task dev.
The UI reads from durable local storage instead of waiting on the network. Storage failures are surfaced, never silently swapped for memory.
Each user begins with a private on-device account, even when the app has no sync service configured.
Users make that same account portable when you connect managed Jazz sync: a passkey where available, a recovery phrase as the portable fallback. Or they sync to a node they run — the app needs zero changes either way.
Columns are sealed on the client before they sync — s.privateTable encrypts by default, and shared sealed columns extend that to groups. The keys stay with users; the server relays what it cannot read.
Tables are the nouns; mutations declared with s.mutation are the verbs. Their sync-boundary effects run from a durable journal on the device that performed the intent — once, even across restarts.
Private resources, direct shares, and fixed-role groups sit behind a small @nzip/lofi/access API. Raw Jazz permissions stay available as an escape hatch.
Generated apps ship a strict Content-Security-Policy, detect a forked storage container before it splits the account, and show honest progress on the one first load that needs the network.
Playwright-backed fixtures cover offline writes, multiple clients, convergence, and readiness — without hand-timed sleeps.
Unsupported browsers get a clear explanation, not a half-working app that risks the user's data.
my-app/
├── deno.json # tasks + one pinned version
├── public/ # manifest, product icons
├── src/
│ ├── app.ts # storage, sync, passkey config
│ ├── schema.ts # persisted data model
│ ├── permissions.ts # private, shared, or group
│ ├── islands/ # interactive Preact UI
│ ├── layouts/ # document shell
│ ├── pages/ # Astro routes
│ └── styles/ # product styling
├── tests/ # local-first journeys
├── .lofi/ # generated tooling (ignored)
└── dist/ # production PWA (ignored)
@nzip/lofi # storage, identity, sync, PWAA generated project keeps your source separate from the versioned framework package. Product work stays in the files above; framework behaviour is imported, never copied into your tree. Upgrading the package updates the runtime without a migration through your own source.
stack and version policy
The data layer is an alpha, so every upgrade is reviewed and validated before it reaches a generated project.
| Layer | Choice | Pinned at |
|---|---|---|
| Data / sync | Jazz 2, CRDTs, OPFS | 2.0.0-alpha.53 |
| UI runtime | Preact islands, thin adapter | Preact 10 / Astro 7 |
| Shell | Prerendered Astro, static host | Astro 7 |
| Toolchain | Deno tasks, npm compat | Deno 2.9 |
Built for mobile web apps where offline is a requirement, not a best-effort enhancement. Before you commit:
Every generated project exposes these as deno task <name>.
dev | Runs the Astro dev server and prints runtime state. |
doctor | Checks readiness without printing config or secrets. |
test | Runs the deterministic local-first suite. |
build | Creates a static production build in dist/. |
preview | Serves the production build locally. |
jazz:provision | Creates a managed Jazz app and configures .env. |
schema:validateschema:deploy | Validates and publishes the Jazz schema. |
migrations:createmigrations:push | Authors and pushes schema migrations. |
deploydeploy:create | Hosts the static build on Deno Deploy. |