CommentSectionProvider loads and manages all comment state for a given entity. Descendant components access this state through useCommentSection.
Setting Up CommentSectionProvider
Identify the target entity with one of four mutually exclusive props:Which entity the section uses
The section can be handed an entity in two ways: theentity prop, or, when there’s no entity prop, the entity of an EntityProvider it’s rendered inside.
- It follows that entity as it changes. A new object for the same entity, such as one with an updated
commentsCount, updates theentityreturned byuseCommentSectionand nothing else: the loaded comments, replies and sort stay as they are and nothing is fetched again. Only a different entityidresets the section and loads that entity’s comments. - An id prop decides which entity is wanted. With
entityId,foreignIdorshortIdset, a handed entity is used only if it is that entity. One that doesn’t match is ignored, and the section fetches the entity the id names. - A handed entity that goes away is kept. If the
entityprop or theEntityProviderentity becomesundefined, the section keeps showing the last entity it had until it’s given another.
Provider Props Reference
string
Sublay entity ID. Mutually exclusive with
foreignId, shortId, and entity.string
Your system’s ID for the entity. The section finds the entity that has it; it creates one only with
createIfNotFound.string
Auto-generated short ID.
Entity
A pre-fetched entity object. Skips the entity fetch. The section follows it as it changes: see Which entity the section uses.
boolean
Used with
foreignId. Default: false. When true, the entity is created the first time the section loads if no entity with that foreignId exists yet. Without it, the section only finds an existing entity: when there is none, error is set and no comments load.number
Top-level comments per page. Default:
15.string
Initial sort order:
"top" (default), "createdAt", or "controversial". Changeable at runtime with setSortBy. "new"/"old" are deprecated aliases for "createdAt"."asc" | "desc"
Initial sort direction; applies to every sort. Default:
"desc". Changeable with setSortDir.ReactionType
The reaction
"top" ranks by — the raw count of that reaction. Server default: "upvote". Changeable with setSortByReaction.number
Levels of replies to bring with every page of top-level comments (
0–5). Default: 0 — replies load on demand through useReplies. See Nested replies.number
Replies per comment in the nested levels. Default:
3."createdAt" | "top" | "controversial"
Sort for replies — both the nested ones and those loaded through
useReplies. Defaults to the current section sort."asc" | "desc"
Sort direction for replies. Defaults to the current section sort direction.
ReactionType
The reaction replies rank by when their sort is
"top". Defaults to the section’s sortByReaction when the section is also sorted by "top", else "upvote"."user" | "grants" | ("user" | "grants")[]
Related data to populate on every comment, at every level — the highlighted comment included. Default:
"user".The
reply* props are fixed for the provider’s lifetime — changing one reloads the section like any other prop change. Only the section’s own sort (setSortBy, setSortDir, setSortByReaction) changes at runtime.string
If provided, fetches this comment (and its parent, for a reply) and adds it to the section. Useful for deep-linking to a specific comment. A deleted or removed comment is highlighted as its placeholder. If the comment doesn’t exist, is on an entity whose author has blocked the user, or belongs to a different entity than the section’s,
highlightedComment stays null and the section loads normally.Record<string, function>
Optional callback functions for handling UI interactions:
loginRequiredCallback()— called when a non-authenticated user tries to commentusernameRequiredCallback()— called when the user has no username setcommentTooShortCallback()— optional. Called when the submitted comment is too short; without it the comment is just not postedcommentCreateFailedCallback({ code?, message?, status?, parentId })— called whencreateCommentsent a comment and the request failed.codeandmessagecome from the server’s error response andstatusis its HTTP status — includinguser/suspendedwith403when the user is suspended; all three areundefinedonly when no response arrived (a network error).parentIdis the comment being replied to, ornullfor a top-level comment. The same details are increateError.
{ user?: string; space?: string }
Override the trigger characters for mentions. Defaults:
{ user: "@", space: "#" }.Nested Replies
By default a section loads top-level comments only, and each comment’s replies load on demand. SetreplyDepth to bring a few levels of replies with every page, so a threaded view renders its first screen with one request:
CommentThread works without replyDepth: every comment then starts with no replies loaded, and the first click fetches them. useReplies continues from whatever is already loaded and nests on a comment’s first load — see useReplies for the details.
Each page can return at most 500 comments, nested replies included (limit × (1 + replyLimit + … + replyLimit^replyDepth)). The section sends its props as given; a combination over the limit lands in error, and a development build logs which values to lower.
Show counts from repliesCount (for example “12 replies”) and drive “load more” buttons from hasMore.
Blocked authors
When themoderation bundle is installed, comments carry two flags you should render (see Blocked authors):
isUnavailable— show a neutral placeholder row with no reply or reaction controls (the server refuses both). Check it before deleted/removed markers.isAuthorBlocked— the user blocked this author; the full comment is there. Collapse it with a “show anyway” option, or hide it — replying and reacting still work.
Accessing State with useCommentSection
useCommentSection Return Values
Entity | null | undefined
The entity the section is on.
undefined until it is known: while the section looks it up from entityId, foreignId or shortId, and when that lookup failed (see error). Its commentsCount includes a comment that is being posted through createComment.Comment[]
Paginated root-level comments (not newly submitted ones).
Comment[]
Comments submitted in this session. Displayed separately from paginated results, sorted newest-first.
{ comment: Comment; parentComment: Comment | null } | null
The highlighted comment (and its parent, if any) when
highlightedCommentId is set. Both are read from the section, so an edit or delete shows here at once. null again when highlightedCommentId is cleared or the section moves to another entity.boolean
true while the comment named by highlightedCommentId is being fetched, until it’s found, not found, or the fetch fails. true again when highlightedCommentId changes or the section moves to another entity, until that fetch finishes. false when highlightedCommentId isn’t set. Use it to tell “still loading” apart from “not found” (highlightedComment is null in both).boolean
true while a page of top-level comments is loading, and while the section is looking up its entity. false when the entity lookup failed. When the section moves to another entity, comments is empty and loading is true from the first render with that entity.Error | null
The last failure loading the section: the comment list, or the entity itself when the section looks it up from
entityId, foreignId or shortId and it can’t be fetched or isn’t found. Cleared when the next load starts. Call loadMore() to retry — hasMore stays true after a failure, and the retry asks for the same page again, including a first page that failed. After a failed entity lookup, loadMore() looks the entity up again. A missing highlighted comment doesn’t set it.boolean
true when more top-level comments are available.boolean
true while a createComment call is in flight.CommentCreateError | null
The last failure creating a comment through
createComment: { code?: string; message?: string; status?: number; parentId: string | null }. code and message come from the server’s error response (see Create Comment → Error Responses) and status is its HTTP status; all three are undefined only when no response arrived (a network error). A comment refused because the user is suspended has code: "user/suspended" and status: 403. parentId is the comment that was being replied to, or null for a top-level comment. Set when a create fails; cleared when the next create starts and when the section moves to another entity. A create stopped before it is sent — no signed-in user, no username, a comment that is too short — doesn’t change it.() => Promise<void>
Loads the next page of top-level comments (with the section’s
replyDepth), or retries the page that failed. When the entity lookup failed, it retries that lookup first; the comments then load on their own once the entity is found. Does nothing while a page is loading or when hasMore is false, and never throws — failures land in error.(commentId: string) => Comment | undefined
A loaded comment by ID — top-level or a reply — without its nested
replies.CommentsSortByOptions
Current sort:
"createdAt", "top", or "controversial". ("new"/"old" are deprecated aliases for "createdAt".)(newSortBy: CommentsSortByOptions) => void
Changes the sort order and reloads from the first page.
"asc" | "desc"
Current sort direction; applies to every sort.
(newSortDir: "asc" | "desc") => void
Changes the sort direction and reloads from the first page.
ReactionType | undefined
The reaction
"top" ranks by. Only sent with sortBy: "top".(newReaction: ReactionType | undefined) => void
Changes the ranking reaction. While
sortBy is "top" this reloads the section; under any other sort the value is kept for later and nothing reloads.(props) => Promise<Comment | undefined>
Submits a new comment. Shows it immediately (optimistically) and adds 1 to the parent comment’s
repliesCount and the entity’s commentsCount, rolling back if the request fails. Accepts:parentId— reply to a specific comment IDcontent— text contentgif— GIF data objectmentions— array ofMentionobjectsautoReaction— automatically react with this reaction type after creating (e.g."upvote")
undefined when nothing was posted: no signed-in user, no username (when usernameRequiredCallback is set), a comment that is too short, no entity yet, another create still in flight, or a request that failed. When the request fails it also sets createError and calls callbacks.commentCreateFailedCallback.Where the commentsCount + 1 goes depends on where the section’s entity comes from:- An
EntityProviderentity — when it is the section’s entity, the provider’s entity is updated, so the rest of the page sees the count too. - An entity the section fetched (
entityId,foreignIdorshortId) — the section’s own copy is updated. - An
entityprop — the object you pass is not modified. The count shows on theentitythatuseCommentSectionreturns. Once the comment is posted, it stays until the entity you pass changes itscommentsCount— up or down — or you pass a different entity; from then on the section shows the count you pass.
comment/parent-deleted) or removed by a moderator (comment/parent-removed). When a reply fails with one of these codes, the section also updates its copy of that parent to the placeholder the server returns for it: a deleted comment gets userDeletedAt and its blanked fields; a removed comment gets moderationStatus: "removed" and the same blanked fields, or keeps its content when the user is its author.(props: { commentId: string; content: string }) => Promise<void>
Updates a comment’s text content.
(props: { commentId: string }) => Promise<void>
Deletes a comment. It stays in place as a placeholder, so the replies under it remain;
repliesCount and commentsCount don’t change, since the server keeps counting it.Comment | null
Currently selected/focused comment. Managed via
setSelectedComment. Cleared when the section moves to another entity.(comment: Comment | null) => void
Set or clear the selected comment.
Partial<Comment> | null
The comment currently being replied to.
createComment uses it as the parent when no parentId is passed, and clears it (with the reply banner and pushMention) when it sends. If the request fails, repliedToComment is put back, so sending again is still a reply to the same comment. Its user is then the user the banner named — the pushed mention when there was one, otherwise the comment’s author — and showReplyBanner is put back when there is such a user. pushMention stays cleared. repliedToComment is also cleared when the section moves to another entity.(comment: Comment | null) => void
Set the comment to reply to.
boolean
Whether to show a reply indicator UI banner. Set back to
false when the section moves to another entity.({ newState }: { newState: boolean }) => void
Show or hide the reply banner.
User | null
A user to auto-insert as a mention in the next comment. Set by
handleShallowReply. Cleared when the section moves to another entity.(comment: Comment) => void
Sets the reply target for a deep reply (reply appears nested under the parent comment).
(comment: Comment) => void
Reply at the same level as
comment: the reply target becomes comment’s own parent, and comment’s author is set as pushMention. For a top-level comment there is no parent, so the reply is top-level too.
