Discussion

Threaded discussion / commenting component. Two-level threading (top-level posts and replies), four sort modes, cursor-based pagination, reactions, post reporting, and authentication-aware UI.

Import

import { Discussion } from "fansunited-frontend-components";
import { DiscussionProps, DiscussionSortType } from "fansunited-frontend-core";

Required props

PropTypeDescription
entityIdstringUnique discussion identifier (e.g. article ID, match ID).
sdkFansUnitedSDKModelSDK instance.
languageLanguageTypeDisplay language.

Discussion does not take a template prop — its layout is always a single threaded list that adapts to the container width.

Optional props

PropTypeDefaultDescription
themeOptionsCustomThemeOptions—See Theming.
discussionLabelstring—Title used when the discussion is created for the first time.
discussionUrlstring—URL associated with the discussion content (used in shares/back-references).
defaultSortDiscussionSortType"LATEST"Initial sort order.
postsPerPagenumber20Posts loaded per page. 0 hides the post list (including posts the user just created) and the posts count, showing only the title and the composer.
showReactionsbooleantrueShow reaction buttons.
showRepliesbooleantrueEnable reply functionality.
showReportPostbooleantrueShow the report option in the post menu.
showModeratedPostsbooleanfalseDisplay moderated posts with a masked design (content replaced by the moderation reason).
infiniteScrollbooleanfalseLoad more posts on scroll instead of showing a "Load More" button.
showBanHistorybooleanfalseWhen the signed-in user is banned, add a collapsible list of their previous bans to the ban notice.
refreshIntervalnumber0Check for new comments every N milliseconds and add them to the list highlighted as new. 0 disables it; values below 5000 are raised to 5000.
userIsLoggedInbooleanfalseWhether the current user is authenticated.
onSignInClick() => void—Callback when the sign-in prompt is clicked.

Note: Discussion uses a simpler onSignInClick callback, not the full SignInCTADetails object.

Sort modes

type DiscussionSortType = "LATEST" | "OLDEST" | "POPULAR" | "INTERACTED";
ValueOrder
LATESTNewest first (default).
OLDESTOldest first.
POPULARMost-reacted first.
INTERACTEDMost-replied first.

Authentication and sign-in

When userIsLoggedIn={false}:

  • The post composer is replaced with a sign-in prompt.
  • Reactions, replies, delete, and report actions are gated behind authentication.
  • Passing onSignInClick makes the sign-in button active; omitting it shows a disabled button.
<Discussion
  {...otherProps}
  userIsLoggedIn={false}
  onSignInClick={() => openSignInModal()}
/>

Banned users

When the signed-in user is banned from discussions, the widget shows a notice above the composer with the reason, when the ban started, and when it ends (or that it is permanent). The ban is looked up with the user ID taken from the SDK's authProvider token.

While the ban is active, the user can still read the discussion, but the composer, replying, reactions, reporting and deleting their own posts are disabled. Set showBanHistory to also list the user's previous bans in the notice.

<Discussion {...otherProps} userIsLoggedIn={true} showBanHistory={true} />

Live updates

By default, comments are loaded when the page opens and when the sort changes. For live blogs and match threads, set refreshInterval so comments posted by other users appear without a page reload:

<Discussion {...otherProps} refreshInterval={15000} />

Every interval, the widget checks for new comments and adds them to the list highlighted with a colored side border, a tinted background and a "New" badge. The highlight fades a few seconds after the comment has been scrolled into view.

With refreshInterval on, comments get a little padding so the highlight has room; comments don't move when it appears or fades.

  • With the Latest sort, new comments are added at the top. With the other sorts, they are added at the end once all comments are loaded; before that, "Load More" brings them in.
  • Checks pause while the browser tab is in the background and resume immediately when it comes back.
  • Only new top-level comments are added. Replies, reactions and deletions by other users appear after a reload.
  • The posts count in the header goes up by the number of new comments added.

Reactions

Eight reaction types are available when showReactions={true}:

type ReactionType = "LIKE" | "DISLIKE" | "LOVE" | "LAUGH" | "CARE" | "WOW" | "SAD" | "ANGRY";
  • Click an emoji to toggle the reaction.
  • Click the same emoji again to remove it.
  • Only authenticated users can react.

Moderated posts

showModeratedPosts={true} fetches and displays moderated comments with a masked design. The original content is replaced by the moderation reason (or a localised fallback), while avatar, username, timestamp, reactions, and reply button remain visible.

Examples

Basic

<Discussion
  entityId="article-123"
  sdk={sdk}
  language="en"
  discussionLabel="Liverpool vs Man City — Match Reaction"
/>

Authenticated user, full features

<Discussion
  entityId="article-123"
  sdk={sdk}
  language="en"
  discussionLabel="Liverpool vs Man City — Match Reaction"
  discussionUrl="https://your-site.com/articles/liverpool-vs-man-city"
  userIsLoggedIn={true}
  defaultSort="POPULAR"
  postsPerPage={10}
  showReactions
  showReplies
  showReportPost
  themeOptions={{ mode: "light" }}
/>

Minimal read-only style

<Discussion
  entityId="article-123"
  sdk={sdk}
  language="en"
  discussionLabel="Post Comments"
  userIsLoggedIn={false}
  showReactions={false}
  showReplies={false}
  showReportPost={false}
  onSignInClick={() => openSignInModal()}
/>

Dark theme with sign-in integration

<Discussion
  entityId="match-456"
  sdk={sdk}
  language="en"
  discussionLabel="Match Day Chat"
  userIsLoggedIn={isUserLoggedIn}
  onSignInClick={() => router.push("/login")}
  defaultSort="LATEST"
  postsPerPage={20}
  themeOptions={{
    mode: "dark",
    colorSchemes: {
      dark: {
        surface: "#212121",
        textPrimary: "#FAFAFA",
        palette: {
          primary: {
            plainColor: "#2397F3",
            primaryContainer: "#1A77D2",
            onPrimary: "#FFFFFF",
          },
        },
      },
    },
  }}
/>

Did this page help you?