User-Centric React Components
The User-Centric React Components are an npm-package alternative to the iframe-embedded User-Centric Widgets. They render the same widgets (quizzes, polls, predictors, lead forms, discussions, leaderboards, betslip, etc.) directly inside your React application, giving you native control over routing, state, theming, and bundle size.
What's in the box
Two npm packages, designed to be installed together:
| Package | Purpose |
|---|---|
fansunited-frontend-components | The React components themselves (UI). |
fansunited-frontend-core | Shared types, theme tokens, hooks, providers, i18n, helpers, and constants. |
Both packages must be installed.
fansunited-frontend-componentsdoes not bundlefansunited-frontend-core— it imports the shared theme, providers, i18n and helpers from it at runtime, so your app loads a single copy of core.
When to use React components vs. iframe embed
| Scenario | Recommended |
|---|---|
| You build with React (Next.js, Vite, CRA, Remix, etc.) and want native rendering | React components |
| You need to control routing, navigation, or share auth state with the widget | React components |
| You want to ship the SDK only once across many widgets on a page | React components |
| You publish content in a CMS without a React stack (WordPress, plain HTML, etc.) | iframe embed |
| Quick drop-in for marketing/landing pages | iframe embed |
Available components
| Component | Purpose |
|---|---|
ClassicQuizPlay | Traditional quiz with scoring and answer explanations |
PollVote | Real-time polling with single/multiple choice voting |
PersonalityQuizPlay | Personality assessment with persona matching |
MatchQuizPlay | Football match prediction with multiple markets and points |
EventGamePlay | Generic event prediction (boolean/number/enum/free input) |
PickThePair | Pair-matching game with casual and challenge modes |
ChanceGame | Prize Wheel, Penalty Shootout, and Pick One of X variants |
EitherOrPlay | Binary choice game with rounds and standings |
CollectLead | Lead capture forms with custom fields and consents |
Discussion | Threaded comments with reactions and reporting |
Leaderboard | Ranked standings across one or more game entities |
Predictor | Multi-tab seasonal predictor with leaderboard, leagues, prizes — or a single-match "embed" card for articles |
Betslip | Floating betslip with bookmaker deep-link integration |
Standings | Sport-agnostic league/competition standings table with zone highlighting and team form |
Key design principles
- Shadow DOM isolation — every component renders inside its own shadow root. Host page CSS cannot leak in, and component styles cannot leak out.
- Theme-first — every component accepts the same
CustomThemeOptionsobject. Set it once viaThemeProvider, or pass it per component. - i18n built-in — 16 supported languages, including right-to-left Arabic. Pass
languageper component, or initialize globally viainitializeI18n(). - SDK-driven — components consume a
FansUnitedSDKModelinstance. No props are needed for content — entity IDs resolve via the SDK. - Tree-shakeable — import only the components you use.
Supported languages
English (en), Arabic (ar, right-to-left), Bulgarian (bg), German (de), Greek (el), Spanish (es), French (fr), Belgian French (fr-be), Italian (it), Polish (pl), Portuguese (pt), Brazilian Portuguese (pt-br), Romanian (ro), Serbian (sr), Slovak (sk), Turkish (tr).
Peer dependencies
{
"react": ">=16.8.0",
"react-dom": ">=16.8.0",
"fansunited-sdk-esm": ">=1.134.0"
}These are peer-only: the packages never install their own copy, so there is exactly one React
runtime and one SDK in your app. You already install fansunited-sdk-esm to create the SDK
instance you pass to the components.
Installed dependencies
MUI Joy, MUI Material and Icons, Emotion and i18next are regular dependencies of both packages,
declared with identical version ranges, so your package manager installs one copy of each and the
components and core share it (theme, style cache and translations are context-based and must not
be duplicated). You do not need to add them yourself.
Importing only what you need
Core exposes subpath entries, so reading an enum does not load the widget UI:
import { WidgetTemplate } from "fansunited-frontend-core/enums";
import type { LanguageType } from "fansunited-frontend-core/types";Translations load on demand, one chunk per language: only English (the fallback) and the
language a widget renders are downloaded.
Every component has its own entry (fansunited-frontend-components/Predictor, /TopX, …).
Import from these rather than the root when a page uses one or two components, so bundlers that
only drop whole modules do not include every component. Core is published as one file per
module, so even a root import of core costs only the modules a component uses.
The components import SDK enums and models from fansunited-sdk-esm/enums and /models, never
from the SDK root, so rendering a component does not add the full SDK to a page. Create the SDK
with createSDK and the namespaces you use to keep the SDK itself small.
Static artwork
Default backgrounds, placeholder avatars and the score-state icons are served from
https://uploads.fansunitedassets.com instead of being embedded in the JavaScript. If your site
sets a Content Security Policy, img-src must allow *.fansunitedassets.com, which it already
needs for team logos and player photos.
Version reporting
window.fansUnitedComponentsVersions holds the running components and core versions. It is set
when the first component mounts, not when the package is imported.
Next steps
- Getting Started — install, set up the SDK, and render your first component
- Core Concepts — templates, theming, lead collection, CTAs, and other shared configuration
- Components — per-component API references and examples
Updated 6 days ago
