Predictor

Season-long football score predictor with a multi-tab interface: Play, Leaderboard, Private Leagues, Rules, and Prizes. Includes consent gating, custom Play-tab banners, and first-class Betslip integration.

Import

import { Predictor } from "fansunited-frontend-components";
import {
  PredictorProps,
  PredictorTab,
  PredictorBetslipConfig,
  PlayTabBanner,
  ConsentDef,
} from "fansunited-frontend-core";

Required props

PropTypeDescription
entityIdstringPredictor template identifier.
sdkFansUnitedSDKModelSDK instance.
languageLanguageTypeDisplay language.

Predictor does not take a template prop — its layout is a fixed multi-tab interface.

Optional props

PropTypeDefaultDescription
themeOptionsCustomThemeOptionsSee Theming.
userIsLoggedInbooleanfalseHost auth state.
signInCTASignInCTADetailsSee Sign-in CTA.
tabsPredictorTab[]all tabsWhich tabs to enable.
defaultImagePlaceholderUrlstringFallback image URL for the hero header.
playTabBannersPlayTabBanner[]Custom banners injected into the Play tab.
consentsConsentDef[]Consent definitions required before predicting.
matchCardBgImageUrlstringBackground image URL for match prediction cards.
betslipPredictorBetslipConfigBetslip integration. See below.

Tabs

type PredictorTab = "play" | "leaderboard" | "private-leagues" | "rules" | "prizes";
TabDescription
playMain prediction interface — submit match score forecasts.
leaderboardGlobal rankings with pagination and user highlight.
private-leaguesCreate and join private leagues.
rulesSearchable game rules and scoring system.
prizesPrize distribution and rewards.

Pass tabs to limit the visible tabs:

<Predictor {...otherProps} tabs={["play", "leaderboard"]} />

Play tab banners

Inject custom React content into specific slots inside the Play tab.

type PlayTabBannerPosition =
  | "above-group-nav"
  | "below-group-nav"
  | "below-matches"
  | "bottom"
  | "left"
  | "right";

interface PlayTabBanner {
  position: PlayTabBannerPosition;
  render: () => React.ReactNode;
}
PositionLocation
above-group-navAbove the matchweek group navigation.
below-group-navBelow the matchweek group navigation.
below-matchesBelow the list of match cards.
bottomVery bottom of the Play tab.
leftLeft sidebar (desktop only).
rightRight sidebar (desktop only).
<Predictor
  {...otherProps}
  playTabBanners={[
    { position: "above-group-nav", render: () => <SponsorBanner /> },
    { position: "right", render: () => <AdSlot id="predictor-sidebar" /> },
  ]}
/>

Consent gating

interface ConsentDef {
  consentId: string;
  body: string;        // HTML allowed — links, formatting
  required: boolean;
  defaultChecked: boolean;
}
  • Required consents block prediction submission until accepted.
  • Optional consents appear in the same modal but do not block progression.
  • Once accepted, the modal does not reappear for that user.
<Predictor
  {...otherProps}
  consents={[
    {
      consentId: "tos",
      body: 'I accept the <a href="https://example.com/tos" target="_blank">Terms of Service</a>.',
      required: true,
      defaultChecked: false,
    },
  ]}
/>

Betslip integration

When the betslip prop is provided, Predictor renders a Betslip widget internally as a sibling — you do not add a separate <Betslip /> component to the page. See the Betslip component page for the standalone widget reference.

interface PredictorBetslipConfig {
  trigger?: PredictorBetslipTrigger; // "predictions-only" | "odds-only"
  position?: BetslipPosition;
  maxSelections?: number;
  stakePresets?: number[];
  oddsPollingInterval?: number;
  currency?: string;
  ctaUrlTemplate?: string;
  brandingLogoUrl?: string;
  labels?: BetslipLabels;
  themeOptions?: CustomThemeOptions;
}

Trigger modes

ModeWhen selections are sent to Betslip
"predictions-only" (default)Every time the user edits a score (home/away increment or decrement). If the user submits without editing (e.g. default 0-0), the selection is sent at submit time as a fallback. Outcome derivation: "1" (home win), "X" (draw), "2" (away win).
"odds-only"Only when the user clicks an odds button on the post-submit odds display. The odds button becomes a command-bus trigger instead of a direct bookmaker link.

Selection format sent by Predictor

"{matchId}:FT_1X2:{outcomeKey}"
// Example: "fb:m:451634:FT_1X2:1"  (home win)

Automatic removal

When the user deletes a submitted prediction, the matching betslip selection is removed via betslipApi.removeSelection(). When a score is edited in "predictions-only" mode after submission, the new outcome upserts the old one (no explicit removal needed).

Theme inheritance

If betslip.themeOptions is not provided, the Betslip widget inherits the Predictor's own themeOptions. A single theme on the Predictor is enough to style both consistently.

Examples

Basic

<Predictor
  entityId="predictor-template-123"
  sdk={sdk}
  language="en"
/>

Authenticated with selected tabs

<Predictor
  entityId="predictor-template-123"
  sdk={sdk}
  language="en"
  userIsLoggedIn
  tabs={["play", "leaderboard", "private-leagues"]}
  matchCardBgImageUrl="https://your-cdn.com/stadium-bg.jpg"
  themeOptions={{ mode: "dark" }}
/>

Full setup with banners and consents

<Predictor
  entityId="predictor-template-123"
  sdk={sdk}
  language="en"
  userIsLoggedIn={false}
  signInCTA={{ defaultLabel: "Sign in to Predict", onClick: handleSignIn }}
  tabs={["play", "leaderboard", "private-leagues", "rules", "prizes"]}
  matchCardBgImageUrl="https://your-cdn.com/matchcard-bg.jpg"
  playTabBanners={[
    { position: "above-group-nav", render: () => <SponsorBanner /> },
    { position: "right", render: () => <AdSlot id="predictor-sidebar" /> },
  ]}
  consents={[
    {
      consentId: "tos",
      body: 'I accept the <a href="https://example.com/tos" target="_blank">Terms of Service</a>.',
      required: true,
      defaultChecked: false,
    },
  ]}
  themeOptions={{ mode: "light" }}
/>

Predictor + Betslip (predictions trigger)

<Predictor
  entityId="predictor-template-123"
  sdk={sdk}
  language="en"
  userIsLoggedIn
  tabs={["play", "leaderboard"]}
  betslip={{
    trigger: "predictions-only",
    position: "side-right",
    currency: "£",
    stakePresets: [5, 10, 25, 50],
    ctaUrlTemplate:
      "https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}&ref={currentUrl}",
    labels: { disclaimer: "18+ | Please gamble responsibly" },
  }}
  themeOptions={{ mode: "dark" }}
/>

Predictor + Betslip (odds trigger)

<Predictor
  entityId="predictor-template-123"
  sdk={sdk}
  language="en"
  userIsLoggedIn
  betslip={{
    trigger: "odds-only",
    position: "bottom-right",
    currency: "€",
    ctaUrlTemplate:
      "https://your-bookmaker.com/bet?ids={selectionIds}&stake={stake}",
  }}
/>