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

# Quickstart

> Build your first Fanfare integration with React and the public journey model.

This guide builds a React integration with `ExperienceWidget`, then shows where to switch to `useExperienceJourney` when you need custom UI.

## Prerequisites

* Node.js 18 or later.
* A Fanfare `organizationId`.
* A browser-safe Fanfare `publishableKey`.
* An experience ID such as `exp_123`.

## Step 1: Install The React SDK

```bash theme={null}
pnpm add @fanfare-io/fanfare-sdk-core @fanfare-io/fanfare-sdk-react motion
```

Import the default styles once in the part of your app that renders Fanfare components:

```tsx theme={null}
import "@fanfare-io/fanfare-sdk-react/styles";
```

## Step 2: Add The Provider

Wrap the part of your app that uses Fanfare:

```tsx theme={null}
import { FanfareProvider } from "@fanfare-io/fanfare-sdk-react";
import "@fanfare-io/fanfare-sdk-react/styles";
import { ProductLaunchPage } from "./product-launch-page";

export function App() {
  return (
    <FanfareProvider
      organizationId="org_123"
      publishableKey="pk_live_123"
    >
      <ProductLaunchPage />
    </FanfareProvider>
  );
}
```

Use test credentials while developing locally.

## Step 3: Render The Experience

`ExperienceWidget` renders the supported journey UI for the current customer state.

```tsx theme={null}
import { ExperienceWidget } from "@fanfare-io/fanfare-sdk-react";

export function ProductLaunchPage() {
  return (
    <main>
      <h1>Limited release</h1>
      <p>Join the launch experience when access opens.</p>

      <ExperienceWidget
        experienceId="exp_123"
        autoStart
        checkoutUrl="/checkout"
      />
    </main>
  );
}
```

Use `checkoutUrl` when the default granted state can navigate directly to your next step.

## Step 4: Add A Server Handoff

When your app needs to prepare checkout, use `onGranted`. Keep the grant out of URLs, console logs, and third-party analytics.

```tsx theme={null}
<ExperienceWidget
  experienceId="exp_123"
  autoStart
  onGranted={async (admissionGrant) => {
    await fetch("/api/fanfare/admission", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ admissionGrant }),
    });

    window.location.assign("/checkout");
  }}
/>
```

Your backend should validate the grant before completing protected actions.

## Step 5: Customize Specific States

Use slots when the default widget works but one state needs custom markup.

```tsx theme={null}
<ExperienceWidget
  experienceId="exp_123"
  autoStart
  slots={{
    granted: ({ grant, expiresAt }) => (
      <CheckoutPanel
        admissionGrant={grant}
        admissionGrantExpiresAt={expiresAt}
      />
    ),
  }}
/>
```

Slots keep the widget orchestration while letting your app own important brand or routing details.

## Step 6: Move To Custom React UI When Needed

Use `useExperienceJourney` when you want React lifecycle handling but fully custom screens.

```tsx theme={null}
import { useExperienceJourney } from "@fanfare-io/fanfare-sdk-react";

export function CustomLaunch({ experienceId }: { experienceId: string }) {
  const { view, start, error } = useExperienceJourney(experienceId);

  if (error) return <ErrorPanel message={error} />;
  if (!view) return <LoadingPanel />;

  if (view.journeyStage === "ready") {
    return <button onClick={() => void start()}>Enter</button>;
  }

  if (view.journeyStage === "routing") {
    return <LoadingPanel />;
  }

  if (view.journeyStage === "gated") {
    return <GatePanel view={view} />;
  }

  if (view.sequence.phase === "granted") {
    return (
      <CheckoutPanel
        admissionGrant={view.sequence.grant.token}
        admissionGrantExpiresAt={view.sequence.grant.expiresAt}
      />
    );
  }

  return <SequencePanel sequence={view.sequence} />;
}
```

The important rule is the same for every path: render from the current view and call only actions available on that view.

## What Happens Behind The Scenes

1. `FanfareProvider` initializes the SDK with your browser-safe credentials.
2. The widget or hook gets a journey for the experience ID.
3. The journey resolves the customer's public state.
4. Your UI renders ready, routing, gated, routed, granted, ended, or unavailable states.
5. When granted, your app sends the grant to the next trusted step.

The SDK docs explain the public contract in more detail:

* [Journey State](/sdk/core/journey-state)
* [React Components](/sdk/components/react)
* [React Widget Example](/sdk/examples/react-widget)

## Test Locally

* Use a test experience and test publishable key.
* Visit the page in a clean browser profile.
* Confirm the widget renders a public state.
* Confirm admitted-state handling sends the grant only to your own app or backend.
* Repeat with an existing browser session.

For broader guidance, see [Testing Your Integration](/getting-started/testing).
