Top X
Season-long competitive picks predictor with a multi-tab interface: Play, Leaderboard, Private Leagues, Rules, and Prizes. Users pick their top performers for each gameweek (e.g. top 3 scorers) from a curated fixture list. Includes game navigation label control, consent gating, custom Play-tab banners, and first-class Betslip integration.
Import
import { TopX } from "fansunited-frontend-components";
import {
TopXProps,
TopXTab,
TopXGameNavLabel,
TopXBetslipConfig,
PlayTabBanner,
ConsentDef,
} from "fansunited-frontend-core";Required props
| Prop | Type | Description |
|---|---|---|
entityId | string | Top X template identifier. |
sdk | FansUnitedSDKModel | SDK instance. |
language | LanguageType | Display language. |
TopX does not take a
templateprop — its layout is a fixed multi-tab interface.
Optional props
| Prop | Type | Default | Description |
|---|---|---|---|
themeOptions | CustomThemeOptions | — | See Theming. |
userIsLoggedIn | boolean | false | Host auth state. |
signInCTA | SignInCTADetails | — | See Sign-in CTA. |
tabs | TopXTab[] | all tabs | Which tabs to enable. |
defaultImagePlaceholderUrl | string | — | Fallback image URL for the hero header. |
playTabBanners | PlayTabBanner[] | — | Custom banners injected into the Play tab. |
consents | ConsentDef[] | — | Consent definitions required before predicting. |
matchCardBgImageUrl | string | — | Background image URL for match prediction cards. |
betslip | TopXBetslipConfig | — | Betslip integration. See below. |
showCountdown | boolean | true | Show predictions-close countdown on open games. |
gameNavLabel | TopXGameNavLabel | "gameWeek" | How each game is labelled in the navigation header. See below. |
Tabs
type TopXTab = "play" | "leaderboard" | "private-leagues" | "rules" | "prizes";| Tab | Description |
|---|---|
play | Main prediction interface — submit top picks for each game. |
leaderboard | Global rankings with per-game scope picker for settled games. |
private-leagues | Create and join private leagues. |
rules | Searchable game rules and scoring system. |
prizes | Prize distribution and rewards. |
Pass tabs to limit the visible tabs:
<TopX {...otherProps} tabs={["play", "leaderboard"]} />Game navigation label
The Play tab shows a navigation header with previous/next arrows so users can move between games. gameNavLabel controls what appears as the title in that header.
type TopXGameNavLabel = "gameWeek" | "title" | "titleAndDescription";| Value | What is shown | Falls back to |
|---|---|---|
"gameWeek" (default) | Localised "Gameweek 1", "Gameweek 2"… The index is 1-based and reflects the game's position in the template order. Fully localised — e.g. Spielwoche 3 (de), Jornada 3 (es), Giornata 3 (it). | — |
"title" | The game's title field from the API. No description shown. | Localised gameweek label when the game has no title. |
"titleAndDescription" | The game's title field, and its description field (HTML supported) rendered below the title when present. | Localised gameweek label when the game has no title. |
When to use each value:
"gameWeek"— use when games don't carry meaningful API titles, or when you want consistent localised labels across all clients regardless of API data quality."title"— use when the API data contains curated game names (e.g. "Matchday 33 — Title Race Decider") and you want to surface them, but the description is not needed."titleAndDescription"— use when games carry editorial descriptions that add context, such as prize information or special rules for that round.
// Default — "Gameweek 1", "Gameweek 2"… (no prop needed)
<TopX entityId="topx-123" sdk={sdk} language="en" />
// Show the game's API title only
<TopX entityId="topx-123" sdk={sdk} language="en" gameNavLabel="title" />
// Show the game's API title and description
<TopX entityId="topx-123" sdk={sdk} language="en" gameNavLabel="titleAndDescription" />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;
}<TopX
{...otherProps}
playTabBanners={[
{ position: "above-group-nav", render: () => <SponsorBanner /> },
{ position: "right", render: () => <AdSlot id="topx-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.
<TopX
{...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, TopX 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 TopXBetslipConfig {
trigger?: TopXBetslipTrigger; // "predictions-only" | "odds-only"
position?: BetslipPosition;
maxSelections?: number;
stakePresets?: number[];
oddsPollingInterval?: number;
currency?: string;
ctaUrlTemplate?: string;
brandingLogoUrl?: string;
labels?: BetslipLabels;
themeOptions?: CustomThemeOptions;
}Trigger modes
| Mode | When selections are sent to Betslip |
|---|---|
"predictions-only" (default) | Every time the user edits a pick. |
"odds-only" | Only when the user clicks an odds button on the post-submit odds display. |
Theme inheritance
If betslip.themeOptions is not provided, the Betslip widget inherits the TopX themeOptions. A single theme on TopX is enough to style both consistently.
Examples
Basic
<TopX
entityId="topx-template-123"
sdk={sdk}
language="en"
/>Authenticated with selected tabs
<TopX
entityId="topx-template-123"
sdk={sdk}
language="en"
userIsLoggedIn
tabs={["play", "leaderboard", "private-leagues"]}
matchCardBgImageUrl="https://your-cdn.com/stadium-bg.jpg"
themeOptions={{ mode: "dark" }}
/>Custom game navigation label
// Games have editorial titles and descriptions in the API
<TopX
entityId="topx-template-123"
sdk={sdk}
language="en"
gameNavLabel="titleAndDescription"
/>Full setup with banners and consents
<TopX
entityId="topx-template-123"
sdk={sdk}
language="en"
userIsLoggedIn={false}
signInCTA={{ defaultLabel: "Sign in to Predict", onClick: handleSignIn }}
tabs={["play", "leaderboard", "private-leagues", "rules", "prizes"]}
gameNavLabel="titleAndDescription"
showCountdown
matchCardBgImageUrl="https://your-cdn.com/matchcard-bg.jpg"
playTabBanners={[
{ position: "above-group-nav", render: () => <SponsorBanner /> },
{ position: "right", render: () => <AdSlot id="topx-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" }}
/>TopX + Betslip
<TopX
entityId="topx-template-123"
sdk={sdk}
language="en"
userIsLoggedIn
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" }}
/>Multi-game football score-prediction tournament with a multi-tab interface: Play, Leaderboard, Private Leagues, Rules, and Prizes. Matches are grouped into discrete games each with its own cutoff, golden-goal tiebreaker, and settlement. Includes consent gating, custom Play-tab banners, countdown timer, and first-class Betslip integration.
Import
import { TopX } from "fansunited-frontend-components";
import {
TopXProps,
TopXTab,
TopXBetslipConfig,
PlayTabBanner,
ConsentDef,
} from "fansunited-frontend-core";Required props
| Prop | Type | Description |
|---|---|---|
entityId | string | TopX template identifier. |
sdk | FansUnitedSDKModel | SDK instance. |
language | LanguageType | Display language. |
TopX does not take a
templateprop — its layout is a fixed multi-tab interface.
Optional props
| Prop | Type | Default | Description |
|---|---|---|---|
themeOptions | CustomThemeOptions | — | See Theming. |
userIsLoggedIn | boolean | false | Host auth state. |
signInCTA | SignInCTADetails | — | See Sign-in CTA. |
tabs | TopXTab[] | all tabs | Which tabs to enable. |
defaultImagePlaceholderUrl | string | — | Fallback image URL for the hero header. |
playTabBanners | PlayTabBanner[] | — | Custom banners injected into the Play tab. |
consents | ConsentDef[] | — | Consent definitions required before predicting. |
matchCardBgImageUrl | string | — | Background image URL for match prediction cards. |
showCountdown | boolean | true | Show the kickoff countdown timer on match cards for open games. |
betslip | TopXBetslipConfig | — | Betslip integration. See below. |
Tabs
type TopXTab = "play" | "leaderboard" | "private-leagues" | "rules" | "prizes";| Tab | Description |
|---|---|
play | Main prediction interface — browse games by day and submit score forecasts. |
leaderboard | Global rankings with pagination and user highlight. |
private-leagues | Create and join private leagues. |
rules | Searchable game rules and scoring system. |
prizes | Prize distribution and rewards. |
Pass tabs to limit the visible tabs:
<TopX {...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;
}| Position | Location |
|---|---|
above-group-nav | Above the game-day navigation. |
below-group-nav | Below the game-day navigation. |
below-matches | Below the list of match cards. |
bottom | Very bottom of the Play tab. |
left | Left sidebar (desktop only). |
right | Right sidebar (desktop only). |
<TopX
{...otherProps}
playTabBanners={[
{ position: "above-group-nav", render: () => <SponsorBanner /> },
{ position: "right", render: () => <AdSlot id="top-x-sidebar" /> },
]}
/>Countdown timer
When showCountdown is true (the default), each match card displays a live kickoff countdown while the game is open. Set to false to hide it — only the kickoff time will be shown.
<TopX {...otherProps} showCountdown={false} />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.
<TopX
{...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, TopX 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 TopXBetslipConfig {
trigger?: TopXBetslipTrigger; // "predictions-only" | "odds-only"
position?: BetslipPosition;
maxSelections?: number;
stakePresets?: number[];
oddsPollingInterval?: number;
currency?: string;
ctaUrlTemplate?: string;
brandingLogoUrl?: string;
labels?: BetslipLabels;
themeOptions?: CustomThemeOptions;
}Trigger modes
| Mode | When selections are sent to Betslip |
|---|---|
"predictions-only" (default) | Every time the user edits a score (home/away increment or decrement). 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. |
Theme inheritance
If betslip.themeOptions is not provided, the Betslip widget inherits TopX's own themeOptions. A single theme on TopX is enough to style both consistently.
Examples
Basic
<TopX
entityId="top-x-template-123"
sdk={sdk}
language="en"
/>Authenticated with selected tabs
<TopX
entityId="top-x-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
<TopX
entityId="top-x-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"
showCountdown={true}
playTabBanners={[
{ position: "above-group-nav", render: () => <SponsorBanner /> },
{ position: "right", render: () => <AdSlot id="top-x-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" }}
/>TopX + Betslip (predictions trigger)
<TopX
entityId="top-x-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" }}
/>TopX + Betslip (odds trigger)
<TopX
entityId="top-x-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}",
}}
/>