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. |
showCountdown | boolean | true | Show predictions-close countdown on open games. |
gameNavLabel | TopXGameNavLabel | "gameWeek" | How each game is labelled in the navigation header. |
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; // default false
defaultChecked?: boolean; // default false
position?: "before" | "after"; // ClassicQuizPlay only — ignored here
}- 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" }}
/>Updated about 2 hours ago
