List

A responsive grid of mixed content — classic quizzes, personality quizzes, polls, either/or games, match quizzes and event games in one list, each rendered as a cover-image card with its content type, participation count and sponsor logo. Clicking a card plays that content in a modal, or sends the visitor to your own page.

Import

import { List } from "fansunited-frontend-components";
import { ListProps } from "fansunited-frontend-core";

Required props

PropTypeDescription
entityIdstringID of the list whose content is rendered.
sdkFansUnitedSDKModelSDK instance.
languageLanguageTypeDisplay language.

Optional props

PropTypeDescription
themeOptionsCustomThemeOptionsSee Theming.
itemsCountnumberPage size in "load-more" mode; the hard total in "limited" mode. Defaults to 10.
itemsPerRownumberColumns on wide layouts. Defaults to 2. Once the widget is narrower than 600px — a phone, or a narrow column on a desktop page — the grid drops to a single column regardless.
widgetModeListWidgetMode"load-more" (default), "limited" or "paged".
urlsListContentUrlsPer-content-type click-through URLs.
defaultImagePlaceholderUrlstringCover image for items with no artwork of their own. A built-in placeholder is used when unset.

Forwarded to the content played in the modal

These are handed to whichever widget the clicked card opens (ClassicQuizPlay, PersonalityQuizPlay, PollVote, EitherOrPlay, MatchQuizPlay or EventGamePlay). They have no effect when urls turns the list into a navigation list.

PropTypeDescription
userIsLoggedInbooleanWhether the visitor is signed in. See Sign-in CTA.
signInCTASignInCTADetailsShown by the played content when it is sign-in gated.
additionalCTAAdditionalCTADetailsSee Additional CTA.
shareCTAShareCTADetailsShare CTA of a finished classic quiz.
rulesDisplayRulesDisplaySee Rules Display.
leadsLeadsOptionsSee Lead Collection.
consentsConsentsConfigOne set of consents for every game in the list.
chanceGameVariantChanceGameVariantWhich chance game to render for items that have one attached.
showAnswerExplanationsbooleanClassic and personality quizzes.
optionsLayoutOptionsLayoutAnswer layout for quizzes and polls.
imagePosition"left" | "right"Image side of the played content's standard template.
matchQuizListMatchQuizOptionsMatch-quiz display flags.
eventGameListEventGameOptionsEvent-game display flags.
callbacksListCallbacksonFinish / onShare of the played content.

The played content always renders with the STANDARD template.

Content types

The list can contain any entity type the Lists API returns. What the widget does with each:

Entity typeCardModal playback
CLASSIC_QUIZ
PERSONALITY_QUIZ
POLL
EITHER_OR
MATCH_QUIZ
CUSTOM_EVENT_GAME
Everything else (TOP_X, leaderboard templates, bracket and standing games)

Only content that is still open for engagement is listed — an entity whose status is anything other than ACTIVE or OPEN is filtered out, as is every type outside the six above. A list can hold anything the Lists API supports; the widget simply skips what it can't label or open. If nothing in the list qualifies, it renders nothing at all (no empty-state message), so it can sit on a page without leaving a gap.

Note that games (match quizzes and event games) report a lifecycle status rather than ACTIVE, so they appear while OPEN and drop out of the list once they go LIVE, SETTLED or CLOSED. Event games also don't expose a participation count, so their cards render without one.

Widget modes

ModeBehavior
"load-more" (default)Renders itemsCount items and a Load More button under the grid; each click appends another itemsCount, until the list runs out.
"limited"Never renders more than itemsCount items in total. No button.
"paged"Renders one page of itemsCount items with a page navigation (first / previous / numbers / next / last) below the grid. Each page replaces the previous one.

How requests map to what you see

"load-more" and "limited" fetch in blocks of 50 into a buffer and hand itemsCount of them to the grid at a time, so a list that fits in a single request is fetched once and every later click is served from memory. When the buffer does run short, up to three requests are made per click — enough to fill a page out of mostly-closed content without walking the whole collection, which also means a click can come back with fewer than itemsCount items while more exist further down.

"paged" works the other way round: the API page is the widget page (limit: itemsCount), which is what makes the API's numberOfPages a truthful page count for the navigation.

Dynamic vs manual lists

The Lists API applies limit to dynamic lists only. A manual (hand-curated) list ignores it and returns every entity in a single response, reporting numberOfPages: 1 whatever page size is requested.

The widget handles both without configuration:

  • In "paged" mode it notices a response larger than the page size it asked for, keeps the whole list in memory and pages through it locally — so a manual list of 5 entities with itemsCount: 1 still gets a 5-page navigation, with no further requests.
  • In "load-more" and "limited" mode a manual list simply arrives in the first request and every later click is served from the buffer.

One difference worth knowing: on a manual list the page count is computed after closed content is filtered out, so every page is full. On a dynamic list the count comes from the API, which counts unfiltered entities — so an individual page can render fewer than itemsCount cards, or none at all. When that happens the navigation stays on screen so the visitor can move on.

Navigation vs modal playback

With no urls configured, clicking a card opens the content in a modal on the same page.

Setting any URL switches the whole list to navigation:

interface ListContentUrls {
  classicQuizUrl?: string;
  personalityQuizUrl?: string;
  eitherOrUrl?: string;
  pollUrl?: string;
  matchQuizUrl?: string;
  eventGameUrl?: string;
}
  • An item whose own type has a URL navigates to it.
  • {CONTENT_ID} in the URL is replaced with the item's entity id; a URL without the token is used as-is (one landing page for every item of that type).
  • An item whose type has no URL does nothing when clicked — it does not fall back to the modal. Configure a URL for every type your list contains.

Consents

consents is a single ConsentsConfig handed to whichever game the visitor opens — there is no per-content-type variant. The consent text describes the client's data processing rather than an individual quiz or poll, so one set covers the whole list, the same way the consumer project configures consents once per widget instance.

<List
  entityId="your-list-id"
  sdk={sdk}
  language="en"
  consents={{
    items: [
      {
        consentId: "marketing",
        body: 'I agree to receive marketing emails and accept the <a href="{{privacyPolicyUrl}}">privacy policy</a>.',
        required: true,
      },
    ],
    labels: { ctaLabel: "Continue" },
  }}
/>;

Each game records its own submission when the visitor plays it, so a visitor who plays three items from one list produces three participations against the same consent ids. Note that when a game also collects leads, the lead configuration's position wins over each consent's own position — see Lead Collection.

Chance games

Whether a played item ends with a chance game comes from the item's own data — the entity has a related info page carrying the prizes. chanceGameVariant only picks how that info page is presented (PRIZE_WHEEL, the default, PENALTY_SHOOTOUT or PICK_ONE_OF_X), so it's one value for the whole list rather than a per-content-type setting.

<List
  entityId="your-list-id"
  sdk={sdk}
  language="en"
  chanceGameVariant={ChanceGameVariant.PENALTY_SHOOTOUT}
/>;

EITHER_OR items ignore it: EitherOrPlay has no chance-game support, so an either/or game never shows one however the list is configured.

Match quiz and event game options

Match quizzes and event games played in the modal open with everything on. Turn any of it off with:

interface ListMatchQuizOptions {
  showPredictionDetails?: boolean; // default true
  showCountdown?: boolean; // default true
  showTeamLabels?: boolean; // default true
  showPoints?: boolean; // default true
}

interface ListEventGameOptions {
  showPredictionDetails?: boolean; // default true
  showCountdown?: boolean; // default true
  showPoints?: boolean; // default true
}

Examples

Basic

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

<List entityId="your-list-id" sdk={sdk} language="en" />;

Page navigation

<List
  entityId="your-list-id"
  sdk={sdk}
  language="en"
  widgetMode="paged"
  itemsCount={6}
  itemsPerRow={3}
/>;

Three columns, limited to six items

<List
  entityId="your-list-id"
  sdk={sdk}
  language="en"
  itemsPerRow={3}
  itemsCount={6}
  widgetMode="limited"
/>;

Navigating to your own pages

<List
  entityId="your-list-id"
  sdk={sdk}
  language="en"
  urls={{
    classicQuizUrl: "/quizzes/{CONTENT_ID}",
    personalityQuizUrl: "/personality-quizzes/{CONTENT_ID}",
    eitherOrUrl: "/either-or/{CONTENT_ID}",
    pollUrl: "/polls/{CONTENT_ID}",
    matchQuizUrl: "/match-quizzes/{CONTENT_ID}",
    eventGameUrl: "/event-games/{CONTENT_ID}",
  }}
/>;

Playing content in the modal, fully configured

<List
  entityId="your-list-id"
  sdk={sdk}
  language="en"
  itemsPerRow={2}
  itemsCount={12}
  userIsLoggedIn={isLoggedIn}
  signInCTA={{
    defaultLabel: "Sign in to play",
    onClick: () => {
      // Handle sign-in logic
    },
  }}
  showAnswerExplanations
  optionsLayout="twoByTwo"
  leads={{ fields: ["fullName", "email"], position: "after" }}
  consents={{ items: [{ consentId: "marketing", body: "I agree…", required: true }] }}
  matchQuiz={{ showPoints: false }}
  eventGame={{ showCountdown: false }}
  callbacks={{
    onFinish: (result) => console.log("finished", result),
  }}
/>;

Did this page help you?