Betslip Command Bus

betslipApi is a singleton command bus that lets any code — including code that runs before the <Betslip /> widget mounts — add or remove selections programmatically and read the current betslip state.

For the widget itself, see the Betslip component page.

Import

import { betslipApi } from "fansunited-frontend-components";
import type { BetslipState } from "fansunited-frontend-components";

Important: Always import betslipApi from fansunited-frontend-components, not from fansunited-frontend-core. The Betslip widget bundles its own copy of the command bus; importing from the components package guarantees you use the same singleton instance the widget is listening on.

API

setSelection(selectionId: string): void

Add a selection. If the same eventId + market already exists, the outcome is replaced (upsert behavior — no need to remove first).

betslipApi.setSelection("fb:m:451678:FT_1X2:1");

removeSelection(selectionId: string): void

Remove a selection by its exact id. No-op if the selection is not present.

betslipApi.removeSelection("fb:m:451678:FT_1X2:1");

getState(): BetslipState

One-shot snapshot — returns the last known state immediately.

const { selections, stake, totalOdds } = betslipApi.getState();

subscribe(listener): () => void

Reactive subscription — fires whenever selections, stake, or odds change. Returns an unsubscribe function.

const unsubscribe = betslipApi.subscribe((state: BetslipState) => {
  console.log("selections:", state.selections.length);
  console.log("stake:", state.stake);
  console.log("totalOdds:", state.totalOdds);
});

// Stop receiving updates
unsubscribe();

BetslipState shape

interface BetslipState {
  selections: BetslipSelectionInput[]; // current raw selections
  stake: number;                        // parsed numeric stake (0 when empty)
  totalOdds: number;                    // product of all selection decimal odds (0 when none)
}

Selection ID format

"{eventId}:{market}:{outcome}"
SegmentExamples
eventIdfb:m:451678
marketFT_1X2, DOUBLE_CHANCE, OVER_GOALS_2_5, CORRECT_SCORE, PLAYER_SCORE_FIRST_GOAL
outcome1 (home), x (draw), 2 (away), 1x, yes, no, 1-2 (correct score)

Over/Under markets: Use "yes" for Over and "no" for Under (not "over"/"under").

Correct Score market: Outcomes use hyphen notation, e.g. "1-2". The widget also accepts colons.

Pre-mount queuing

Calls to setSelection made before the <Betslip /> component has mounted are queued and automatically replayed once the widget initializes. There is no need to delay external code to wait for the widget.

import { betslipApi } from "fansunited-frontend-components";

// Safe to call before <Betslip /> is rendered
betslipApi.setSelection("fb:m:451678:FT_1X2:1");

// Later, when <Betslip /> mounts, it receives the queued selection
ReactDOM.render(<Betslip sdk={sdk} language="en" />, root);

Typical integration pattern

// 1. Render the Betslip widget once at app root level
<Betslip sdk={sdk} language="en" />

// 2. From anywhere in your app — e.g. a match card — call betslipApi
import { betslipApi } from "fansunited-frontend-components";

function MatchCard({ eventId, market, outcome }) {
  const selectionId = `${eventId}:${market}:${outcome}`;

  return (
    <button onClick={() => betslipApi.setSelection(selectionId)}>
      Add to Betslip
    </button>
  );
}

Use cases for subscribe

  • Syncing a "selection count" badge somewhere else in your app's chrome.
  • Enabling/disabling a custom CTA based on the current selection state.
  • Forwarding stake changes to your own backend for analytics.
  • Persisting the betslip state across page navigations.
const unsubscribe = betslipApi.subscribe((state) => {
  document.title = state.selections.length > 0
    ? `(${state.selections.length}) Your bet — MySite`
    : "MySite";
});