Info:
This is release candidate documentation for Stream Chat React SDK v15. For the latest stable version, see the latest version (v14).

Poll components

Poll renders the poll of a message. It provides PollContext and renders the PollContent override: the question, the options and the actions. The default MessageUI renders it for every message with a poll_id, with the poll from client.polls.fromState(message.poll_id). Polls explains how polls are created, voted on and kept current.

Best practices

  • Read live poll data from poll.state. message.poll is only the copy that came with the message.
  • Replace parts through the override keys below, and keep Poll, which provides the context.
  • Render poll parts inside a message, in a Channel or a Thread. PollActions and PollOptionSelector read the message from useMessageContext() and the user's capabilities from the channel.
  • Keep option texts short, so the message stays readable.
  • A quoted poll has no poll UI. The quoted message shows a poll icon and, when it has no text, the poll's name.

Poll UI customization

Poll is built from these parts. The ones with an override key are replaced through WithComponents; the others through the props of the part that renders them.

Part Override key Rendered by Shows
PollContent PollContent Poll The whole poll: PollHeader, PollOptionList and PollActions
PollHeader PollHeader PollContent The question and how many options can be chosen, or "Vote ended"
PollOptionList none PollContent A PollOptionSelector per option, and a button for the full list
PollOptionSelector PollOptionSelector PollOptionList One option: a checkbox while the user can vote, the text, the votes and a bar
PollOptionsFullList none PollOptionList Every option, in a dialog
PollActions PollActions PollContent The buttons below the options and the dialogs they open
PollCreationDialog PollCreationDialog AttachmentSelector The form that creates a poll; see Attachment Selector

PollContent shows five options, and a button that opens the full list when there are more. Each PollActions button shows only when it applies:

Button Shows when Opens
End Poll The poll is open and the user created it EndPollAlert
View Results The poll has votes PollResults
Suggest an Option The user can vote (cast-poll-vote, poll open), suggestions are allowed, and the poll has fewer than 100 options SuggestPollOptionForm
Add a Comment, or Update Your Comment The poll is open and allows comments AddCommentPrompt
View Comments The poll has comments and the user has query-poll-votes PollAnswerList

Replace a dialog

PollActions takes the dialogs it opens as props. Wrap it to replace one and keep the others, and register the wrapper as the PollActions override. This one replaces the form that suggests an option:

import { useState } from "react";
import type { FormEvent, PropsWithChildren } from "react";
import {
  PollActions,
  useModalContext,
  usePollContext,
  WithComponents,
} from "stream-chat-react";

const SuggestOptionForm = () => {
  const { poll } = usePollContext();
  const { close } = useModalContext();
  const [text, setText] = useState("");

  const submit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    try {
      await poll.createOption({ text: text.trim() });
      close();
    } catch {
      // The option wasn't added. Show the error in the form here.
    }
  };

  return (
    <form className="suggest-option-form" onSubmit={submit}>
      <input
        aria-label="Option"
        onChange={(event) => setText(event.target.value)}
        value={text}
      />
      <button disabled={!text.trim()} type="submit">
        Suggest
      </button>
    </form>
  );
};

const PollActionsWithOwnSuggestion = () => (
  <PollActions SuggestPollOptionForm={SuggestOptionForm} />
);

const overrides = { PollActions: PollActionsWithOwnSuggestion };

export const WithOwnPollSuggestions = ({ children }: PropsWithChildren) => (
  <WithComponents overrides={overrides}>{children}</WithComponents>
);

The dialog's frame stays the SDK's, and useModalContext() closes it. Polls replaces EndPollAlert the same way.

Change the layout

Override PollContent and arrange the parts. PollOptionList takes the full-list dialog as PollOptionsFullList:

import type { PollState } from "stream-chat";
import {
  PollActions,
  PollHeader,
  PollOptionList,
  usePollContext,
  useStateStore,
} from "stream-chat-react";

const optionsSelector = ({ options }: PollState) => ({ options });

const OptionTexts = () => {
  const { poll } = usePollContext();
  const { options } = useStateStore(poll.state, optionsSelector);

  return (
    <ul>
      {options.map((option) => (
        <li key={option.id}>{option.text}</li>
      ))}
    </ul>
  );
};

// Register as the PollContent override.
export const PollContentWithThreeOptions = () => (
  <div className="str-chat__poll">
    <PollHeader />
    <PollOptionList optionsDisplayCount={3} PollOptionsFullList={OptionTexts} />
    <PollActions />
  </div>
);

Props

Poll

Prop Type Default Description
poll Poll required The poll, a Poll from stream-chat. Without one, nothing renders.

PollContent, PollHeader, PollOptionsFullList, PollResults and EndPollAlert take no props; they read the poll from PollContext.

PollOptionList

Prop Type Default Description
optionsDisplayCount number every option How many options to show. With more options, a button opens PollOptionsFullList. PollContent passes 5.
PollOptionsFullList React.ComponentType PollOptionsFullList The dialog content that lists every option.

PollOptionSelector

Prop Type Default Description
option PollOptionResponseData required The option.
displayAvatarCount number none How many avatars of the latest voters to show. They show only when votes are public. PollOptionList passes 3.
voteCountVerbose boolean false Shows "2 votes" instead of "2".

Clicking the option, or pressing Enter or Space on it, casts or removes the user's vote.

PollActions

Prop Type Default Description
AddCommentPrompt React.ComponentType<AddCommentPromptProps> AddCommentPrompt Adds or updates the user's comment.
EndPollAlert React.ComponentType EndPollAlert Confirms ending the poll.
PollAnswerList React.ComponentType<PollAnswerListProps> PollAnswerList Lists the comments.
PollResults React.ComponentType PollResults Shows the results by option.
SuggestPollOptionForm React.ComponentType<SuggestPollOptionFormProps> SuggestPollOptionPrompt Suggests a new option.

The dialogs' own props:

Component Props
AddCommentPrompt messageId: string, the message that carries the poll
PollAnswerList onUpdateOwnAnswerClick: () => void, which switches to AddCommentPrompt
PollCreationDialog close: () => void, which closes the dialog
PollVote vote: PollVoteResponseData, one vote with its author and time
PollVoteListing votes: PollVoteResponseData[]

Poll API and state

poll.state is a store of PollState: the poll's data from the server, such as name, options, vote_counts_by_option, latest_votes_by_option, is_closed and vote_count, plus four values the client derives:

  • ownVotesByOptionId: the user's votes, by option id;
  • ownAnswer: the user's comment;
  • maxVotedOptionIds: the options with the most votes;
  • lastActivityAt: when the client last updated the poll from the server, in Unix nanoseconds.

The Poll instance has the methods query, update, partialUpdate, close, delete, createOption, updateOption, deleteOption, castVote, removeVote, addAnswer, removeAnswer, queryAnswers and queryOptionVotes. castVote, removeVote and close update poll.state before the server answers; see Voting and keeping the poll current.

Warning:

Don't read votes from MessageContext's message.poll. Read poll.state.

PollContext

Poll provides PollContext to its parts. usePollContext() reads it, and throws outside a Poll.

Value Type Description
poll Poll The Poll instance from client.polls.

PollProvider is exported too. It takes poll and renders nothing without one.

Examples

Show the user's votes

import type { PollState } from "stream-chat";
import { usePollContext, useStateStore } from "stream-chat-react";

const ownVotesSelector = ({ options, ownVotesByOptionId }: PollState) => ({
  options,
  ownVotesByOptionId,
});

export const MyVotes = () => {
  const { poll } = usePollContext();
  const { options, ownVotesByOptionId } = useStateStore(
    poll.state,
    ownVotesSelector,
  );
  const chosen = options.filter((option) => ownVotesByOptionId[option.id]);

  if (!chosen.length) return <span>You haven't voted yet.</span>;

  return (
    <span>You voted for {chosen.map(({ text }) => text).join(", ")}.</span>
  );
};

Render it inside a poll part, for example in your own PollContent.

Hook Returns
usePollContext() { poll }; throws outside a Poll
usePollAnswerPagination({ paginationParams? }) the poll's comments, a page at a time
usePollOptionVotesPagination({ paginationParams }) the votes for one option, a page at a time
useCanCreatePoll() whether the composer's poll form can be sent

See Poll hooks and useCanCreatePoll.