# Polls

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

A poll is a question with options that members of a channel vote on. This page covers creating a poll in the composer, how votes reach every client, and the components that render a poll in a message.

## What users get

The user opens the attachment menu and picks Poll. A dialog asks for the question and the options, which they can drag to reorder. They can also allow more than one answer (with a maximum), hide who voted, let others suggest options, and let them add comments. They send, and the poll appears in the channel as a message.

Bob ticks an option. His vote shows at once, with the count next to each option and the avatars of the latest voters. As the poll's creator, the user sees End Poll. Everyone sees View Results once there are votes, and the actions the user allowed: Suggest an Option, Add a Comment and View Comments, which shows the number of comments (View 1 Comment). A poll with more than five options shows the first five and a button that opens the full list.

![A poll message with votes, End Poll, View Results and the comment actions](https://getstream.io/docs-assets/images/19054722c964.png)

## How it works across the layers

- **State.** The composer's `pollComposer` holds the poll being created. Once created, the poll is a `Poll` object kept in `client.polls`, one per poll, with its data in `poll.state`. The message only refers to it, through `message.poll_id`.
- **UI.** `AttachmentSelector` opens `PollCreationDialog`. The default `MessageUI` renders `Poll` for every message with a poll, and `Poll` renders the question, the options and the actions.
- **Layout.** Not involved.

### Creating a poll

1. Picking Poll in the attachment menu resets `pollComposer` and opens `PollCreationDialog`. Each field writes to `pollComposer`, which validates each field as the user types or leaves it: the question is required, options can't be empty or repeat each other, and the maximum number of answers is a number from 2 to 10.
2. The send button is enabled when `pollComposer.canCreatePoll` is true.
3. Sending calls `MessageComposer.createPoll()`, which creates the poll on the server through `client.polls.createPoll()` and sets the composer's `pollId`.
4. Then `MessageComposer.send()` sends a message that carries only the poll. Text the user typed in the composer stays there for their next message.

`MessageComposer.createPoll()` publishes an `api:poll:create:failed` notification when the request fails, and nothing is sent. [Message composer](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/message-composer/#polls) shows the same two steps in code.

### Voting and keeping the poll current

`client.polls` keeps one `Poll` for each poll id. It fills from every loaded message that carries a poll, and from new ones as they arrive. Poll events (`poll.updated`, `poll.closed`, `poll.vote_casted`, `poll.vote_changed`, `poll.vote_removed`) update the matching `Poll`, so every message that shows it re-renders.

Bob's vote goes through `Poll.castVote(optionId, messageId)`. It shows in `poll.state` before the server answers and is undone if the request fails. `Poll.removeVote()` and `Poll.close()` work the same way. When Bob has reached `max_votes_allowed`, `Poll.castVote()` publishes a notification instead of voting. [What's optimistic and what isn't](https://getstream.io/chat/docs/sdk/react/v15/state/writing-messages/whats-optimistic-and-what-isnt/) has the details.

`poll.state` holds the poll's data, such as `vote_counts_by_option`, and a few values derived from it: `ownVotesByOptionId` (Bob's votes), `ownAnswer` (his comment) and `maxVotedOptionIds` (the leading options). Read it from there, not from `message.poll`, which is only the copy that came with the message.

### Rendering a poll

`MessageUI` reads the poll with `client.polls.fromState(message.poll_id)` and renders `<Poll poll={poll} />`. `Poll` provides `PollContext` and renders `PollContent`:

- `PollHeader`: the question and how many options can be chosen.
- `PollOptionList`: a `PollOptionSelector` for each option, with its vote count, a bar and the latest voters. It shows five options, and a button opens `PollOptionsFullList`, which lists all of them in a dialog.
- `PollActions`: the buttons below the options and the dialogs they open: `EndPollAlert`, `PollResults`, `SuggestPollOptionForm`, `AddCommentPrompt` and `PollAnswerList`.

Each action shows only when it applies. End Poll is for the poll's creator while the poll is open. Voting and suggesting options need the `cast-poll-vote` capability and an open poll. View Comments needs `query-poll-votes`.

## Turning it on or off

Polls are on by default. Two sides decide:

- **The client:** `messageComposer.polls.enabled`, `true` by default.
- **The server:** the channel type's `polls` setting. Creating a poll also needs the user's `send-poll` capability.

To turn poll creation off in every composer:

```ts
client.config.set({
  messageComposer: { polls: { enabled: false } },
});
```

Messages that already carry a poll still render it. The attachment menu offers Poll only in channels, not in threads. [Configuration](https://getstream.io/chat/docs/sdk/react/v15/state/state-and-entities/configuration/#who-decides-what) lists every setting both sides decide.

## Customizing

### Replace part of the poll

Five override keys replace parts of the poll UI: `PollContent`, `PollHeader`, `PollOptionSelector`, `PollActions` and `PollCreationDialog`. A replacement reads the poll from `usePollContext()` and subscribes to `poll.state`. This header shows the question and the number of votes:

```tsx
import type { PollState } from "stream-chat";
import {
  Channel,
  MessageComposer,
  MessageList,
  usePollContext,
  useStateStore,
  WithComponents,
} from "stream-chat-react";

const headerSelector = ({ name, vote_count }: PollState) => ({
  name,
  voteCount: vote_count,
});

const PollHeaderWithVotes = () => {
  const { poll } = usePollContext();
  const { name, voteCount } = useStateStore(poll.state, headerSelector);

  return (
    <div className="poll-header">
      <strong>{name}</strong>
      <span>{voteCount} votes</span>
    </div>
  );
};

const overrides = { PollHeader: PollHeaderWithVotes };

export const ChannelWithPollHeader = () => (
  <WithComponents overrides={overrides}>
    <Channel channel={channel}>
      <MessageList />
      <MessageComposer />
    </Channel>
  </WithComponents>
);
```

### Change the poll's layout

Override `PollContent` and put the SDK's parts in the order you want. `PollOptionList` takes `optionsDisplayCount`, the number of options shown before the "more options" button, and `PollOptionsFullList`, the component that lists all of them:

```tsx
import { PollActions, PollHeader, PollOptionList } from "stream-chat-react";

export const PollContentWithTenOptions = () => (
  <div className="str-chat__poll">
    <PollHeader />
    <PollOptionList optionsDisplayCount={10} />
    <PollActions />
  </div>
);
```

Register it with `<WithComponents overrides={{ PollContent: PollContentWithTenOptions }}>`. Without `optionsDisplayCount`, `PollOptionList` shows every option.

### Replace the action dialogs

`PollActions` takes the dialogs it opens as props: `AddCommentPrompt`, `EndPollAlert`, `PollAnswerList`, `PollResults` and `SuggestPollOptionForm`. Wrap it to replace one and keep the rest:

```tsx
import {
  PollActions,
  useModalContext,
  usePollContext,
} from "stream-chat-react";

const ConfirmEndPoll = () => {
  const { poll } = usePollContext();
  const { close } = useModalContext();

  const endPoll = async () => {
    try {
      await poll.close();
      close();
    } catch {
      // The poll stays open. Show the error in the dialog here.
    }
  };

  return (
    <div className="end-poll-alert">
      <p>Nobody can vote after the poll ends.</p>
      <button onClick={endPoll} type="button">
        End the poll
      </button>
      <button onClick={close} type="button">
        Keep it open
      </button>
    </div>
  );
};

export const PollActionsWithCustomEnd = () => (
  <PollActions EndPollAlert={ConfirmEndPoll} />
);
```

Register it as the `PollActions` override. The dialog around `EndPollAlert` stays the SDK's, and `useModalContext()` closes it.

### Create polls from your own UI

`PollCreationDialog` receives `close`. A dialog of your own fills `messageComposer.pollComposer` with `PollComposer.updateFields()`, then calls `MessageComposer.createPoll()` and `MessageComposer.send()`, as [Creating a poll](#creating-a-poll) describes. Get the composer with `useMessageComposerController()`, so the dialog works for the composer it was opened from.

## Reference

- [Poll components](https://getstream.io/chat/docs/sdk/react/v15/component-reference/poll-components/): `Poll`, its parts and `PollContext`.
- [Poll hooks](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/poll-hooks/): `usePollContext` and the other poll hooks.
- [MessageComposer API](https://getstream.io/chat/docs/sdk/react/v15/reference/state/messagecomposer-api/): `pollComposer`, `createPoll()` and `send()`.
- [Configuration tree](https://getstream.io/chat/docs/sdk/react/v15/reference/state/configuration-tree/): `messageComposer.polls`.
- [Attachments and uploads](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/attachments-and-uploads/): the attachment menu that opens the poll dialog.

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/polls/](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/polls/).