Skip to main content
Slots let you replace specific parts of ExperienceWidget while keeping the default journey orchestration. Use slots when most of the widget works for you but one or two states need custom brand treatment.

Slot example

import { ExperienceWidget } from "@fanfare-io/fanfare-sdk-react";

export function LaunchWidget() {
  return (
    <ExperienceWidget
      experienceId="exp_123"
      slots={{
        upcoming: ({ startsAt, canEnter, onEnter }) => (
          <section className="launch-upcoming">
            <h2>Early access opens soon</h2>
            {startsAt ? <p>Starts {startsAt.toLocaleString()}</p> : null}
            {canEnter ? (
              <button onClick={onEnter}>Notify me</button>
            ) : null}
          </section>
        ),
        granted: ({ grant }) => (
          <CheckoutButton grantToken={grant}>
            Continue to checkout
          </CheckoutButton>
        ),
      }}
    />
  );
}
The granted slot’s grant prop is the grant token string (grant?: string), and may be undefined. Pass it to your checkout handoff as a token, not as an object.

Available slots

SlotRendered when
startThe journey is ready and waiting to start.
loadingThe journey or a stage action is loading.
authAuthentication is required.
accessCodeAn access code is required.
challengeAdditional verification is required.
upcomingThe routed sequence is scheduled (not yet open); onEnter joins the waitlist attachment when canEnter is true.
waitlistThe routed sequence is scheduled and the consumer has joined its waitlist attachment (sequence.waitlist.status is waitlisted or notified).
enterableThe sequence is active and the consumer can enter.
participatingThe consumer is participating.
grantedThe consumer has an active grant.
expiredA previous admission is no longer usable.
endedThe experience or sequence has a terminal outcome. Also rendered for phase "unavailable" (a non-terminal placeholder) for convenience.
errorThe widget cannot continue without recovery.
Slot props include the current snapshot, view, and error where relevant, plus state-specific callbacks such as onStart, onEnter, onLeave, or onRetry. Inside the enterable and participating slots the slot’s sequence is a routed SequenceView; participating views expose their live data as the display$ atom (not state$). Subscribe to it (for example with useNanostore) to render real-time fields such as queue position.

Render prop

Use the render prop when you want the widget to create and observe the journey, but you want to own the entire UI for that widget instance.
<ExperienceWidget experienceId="exp_123">
  {({ view, start, isStarting, error }) => {
    if (error) return <ErrorPanel message={error} />;
    if (!view) return <LoadingPanel />;

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

    return <CustomJourneyView view={view} />;
  }}
</ExperienceWidget>
If you need to share journey state across multiple components or routes, use useExperienceJourney directly instead of the render prop.