> ## 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.

# React Components

> Integrate Fanfare with React using FanfareProvider, ExperienceWidget, and useExperienceJourney.

React integrations usually start with `FanfareProvider` and `ExperienceWidget`.

## Install

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

The React package has peer dependencies on `react`, `react-dom`, and `motion`.

## Add the provider

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

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

`FanfareProvider` initializes the core SDK, restores the local session by default, and provides the SDK to hooks and components below it.

## Render the full widget

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

export function ProductLaunchPage() {
  return (
    <ExperienceWidget
      experienceId="exp_123"
      autoStart
      checkoutUrl="/checkout"
    />
  );
}
```

Use `checkoutUrl` for default granted-state navigation.

Use `onGranted` when your application needs to coordinate custom routing or server handoff.

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

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

Avoid logging the grant value to third-party tools or placing it in URLs.

## Build custom React UI

Use `useExperienceJourney` when you want React state and lifecycle handling without the default widget layout.

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

export function CustomExperience({ 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()}>Start</button>;
  }

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

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

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

The hook returns the same public journey model as the core SDK:

| Field      | Use for                                      |
| ---------- | -------------------------------------------- |
| `journey`  | Access to the underlying journey handle.     |
| `view`     | Normal rendering and state-gated actions.    |
| `snapshot` | Debugging or exhaustive inspection.          |
| `error`    | Adapter-level setup or action errors.        |
| `start()`  | Convenience method for starting the journey. |

`useExperienceJourney` is the React-friendly wrapper around the underlying
journey stores. It subscribes to the current `view$` and `snapshot$` values for
you, updates React state when they change, and cleans up those subscriptions
when the component unmounts. Most React UI should render from the returned
`view` instead of subscribing to `journey.view$` directly.
