> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firstflow.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Browser SDK overview

> @firstflow/runtime is a React widget that loads your experiences, evaluates triggers and audience in the browser, renders the active step, and persists progress to your Supabase.

`@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](/self-hosting/quickstart). Your app references it with `"@firstflow/runtime": "workspace:*"` and imports from it directly:

```tsx theme={null}
import { FirstflowProvider, FirstflowWidget } from "@firstflow/runtime";
```

## 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](/react/use-firstflow).

## Minimal setup

```tsx theme={null}
import "@firstflow/runtime/styles.css";
import { FirstflowProvider, FirstflowWidget } from "@firstflow/runtime";
import { createSupabasePersistence } from "@firstflow/runtime/supabase";
import { createClient } from "@supabase/supabase-js";
import experiences from "./firstflow/experiences.json";

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);

export default function App() {
  return (
    <FirstflowProvider
      user={{ id: currentUser.id, traits: { plan: "pro" } }}
      experiences={experiences}
      persistence={createSupabasePersistence(supabase)}
    >
      <YourApp />
      <FirstflowWidget />
    </FirstflowProvider>
  );
}
```

## What's in the package

| Export                                            | Purpose                                                                         |
| ------------------------------------------------- | ------------------------------------------------------------------------------- |
| [`FirstflowProvider`](/react/provider)            | Loads experiences, runs the trigger engine, holds runtime state                 |
| [`FirstflowWidget`](/react/widget)                | The default widget shell that renders the active step                           |
| [`useFirstflow`](/react/use-firstflow)            | Hook for state and actions (`startExperience`, `advance`, `dismiss`, `emit`, …) |
| `BlockRenderer`                                   | Render a node's blocks inside your own [custom shell](/widget-kit/overview)     |
| [`getAnonymousId`](/react/identity)               | Stable per-browser id for unidentified visitors                                 |
| `emitCustomEvent`                                 | Fire a `custom_event` trigger from anywhere                                     |
| `createSupabasePersistence` (`/supabase` subpath) | Default Supabase persistence adapter                                            |

## Mental model

The runtime is the source of truth at runtime; your `experiences.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.

<Note>
  The runtime talks only to your Supabase, through the persistence adapter you
  pass there is no realtime connection or remote API to configure.
</Note>

## Next

<CardGroup cols={2}>
  <Card title="FirstflowProvider" icon="square-code" href="/react/provider">
    Props, persistence, and event handling.
  </Card>

  <Card title="FirstflowWidget" icon="window" href="/react/widget">
    The default shell, minimize, and anchoring.
  </Card>

  <Card title="useFirstflow" icon="anchor" href="/react/use-firstflow">
    Drive the runtime imperatively.
  </Card>

  <Card title="Theming" icon="palette" href="/react/theming">
    CSS variables and class hooks.
  </Card>
</CardGroup>
