@firstflow/runtime is the browser half of FirstFlow. You give it the experiences you authored in git and a persistence adapter; it decides what to show, renders it, and records progress. Everything happens client-side there is no socket and no server deciding eligibility.
You consume it by cloning the repo and building your app inside its workspace (it’s not on npm) see Self-hosting → Quickstart. Your app references it with "@firstflow/runtime": "workspace:*" and imports from it directly:
What it does
- Loads experiences you pass as a prop (imported from
experiences.json). - Evaluates triggers (
page_load,after_idle,custom_event,url_match,chat_opens), frequency caps, and audience conditions in the browser. - Renders the active step’s blocks via the built-in widget, or your own shell.
- Persists in-progress runs so they resume across reloads, through a persistence adapter.
Minimal setup
What’s in the package
Mental model
The runtime is the source of truth at runtime; yourexperiences.json is the source of truth in git. Changing what users see is a code change and deploy, not a dashboard edit. The Supabase calls persist progress they never decide what to show.
The runtime talks only to your Supabase, through the persistence adapter you
pass there is no realtime connection or remote API to configure.
Next
FirstflowProvider
Props, persistence, and event handling.
FirstflowWidget
The default shell, minimize, and anchoring.
useFirstflow
Drive the runtime imperatively.
Theming
CSS variables and class hooks.