# Polls

Messages can contain polls. By default, polls are created via `PollCreationDialog` opened from [`AttachmentSelector`](/chat/docs/sdk/react/v13/components/message-input-components/attachment-selector/). Messages with polls aren’t editable. Only the poll creator can close a poll. The top-level UI component is `Poll`, which renders a header, options list, and actions section.

![](@chat-sdk/react/v13/_assets/message-with-poll.png)

## Best Practices

- Use the default `Poll` UI unless your product needs a distinct voting flow.
- Respect poll permissions (creator-only close, read-only for others).
- Use `poll.state` for live updates instead of `MessageContext.message`.
- Keep poll options concise to avoid scroll-heavy messages.
- Customize `PollActions` only when you need new dialogs or flows.

## Poll UI customization

The following parts of the poll UI can be customized:

- `QuotedPoll` - UI rendered if the poll is rendered in a quoted message
- `PollContent` - component that renders the whole non-quoted poll UI
- `PollHeader` - customizes the topmost part of the poll UI
- `PollOptionSelector` - customizes the individual clickable option selectors
- `PollActions` - customizes the bottom part of the poll UI that consists of buttons that invoke action dialogs

### Poll header customization

```tsx
import { ReactNode } from "react";
import { Channel } from "stream-chat-react";

const PollHeader = () => <div>Custom Header</div>;

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
  <Channel PollHeader={PollHeader}>{children}</Channel>
);
```

### Poll option selector customization

To customize only the option selector, provide a custom `PollOptionSelector`.

```tsx
import { ReactNode } from "react";
import { Channel } from "stream-chat-react";

const PollOptionSelector = () => <div>Custom Option Selector</div>;

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
  <Channel PollOptionSelector={PollOptionSelector}>{children}</Channel>
);
```

### Poll actions customization

`PollActions` controls dialogs/modals for poll interactions. It supports the following actions:

| Action button             | Visible condition                                                                         | Invokes                 |
| ------------------------- | ----------------------------------------------------------------------------------------- | ----------------------- |
| See all options           | option count > 10                                                                         | `PollOptionsFullList`   |
| Suggest an option         | poll is not closed and `poll.allow_user_suggested_options === true`                       | `SuggestPollOptionForm` |
| Add or update own comment | poll is not closed and `poll.allow_answers === true`                                      | `AddCommentForm`        |
| View comments             | `channel.own_capabilities` array contains `'query-poll-votes'` & `poll.answers_count > 0` | `PollAnswerList`        |
| View results              | always visible                                                                            | `PollResults`           |
| End vote                  | owner of the poll                                                                         | `EndPollDialog`         |

**Default PollOptionsFullList**

![](@chat-sdk/react/v13/_assets/poll-option-full-list.png)

**Default SuggestPollOptionForm**

![](@chat-sdk/react/v13/_assets/suggest-poll-option-form.png)

**Default AddCommentForm**

![](@chat-sdk/react/v13/_assets/add-poll-comment-form.png)

**Default PollAnswerList**

![](@chat-sdk/react/v13/_assets/poll-comment-list.png)

**Default PollResults**

![](@chat-sdk/react/v13/_assets/poll-results.png)

**Default EndPollDialog**

![](@chat-sdk/react/v13/_assets/end-poll-dialog.png)

You can override individual dialogs or the whole `PollActions` component:

```tsx
import { ReactNode } from "react";
import { Channel, PollActions } from "stream-chat-react";
import {
  CustomAddCommentForm,
  CustomEndPollDialog,
  CustomPollAnswerList,
  CustomPollOptionsFullList,
  CustomPollResults,
  CustomSuggestPollOptionForm,
} from "./PollActions";

const CustomPollActions = () => (
  <PollActions
    AddCommentForm={CustomAddCommentForm}
    EndPollDialog={CustomEndPollDialog}
    PollAnswerList={CustomPollAnswerList}
    PollOptionsFullList={CustomPollOptionsFullList}
    PollResults={CustomPollResults}
    SuggestPollOptionForm={CustomSuggestPollOptionForm}
  />
);

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
  <Channel PollActions={CustomPollActions}>{children}</Channel>
);
```

### Poll contents layout customization

If you want to restructure the poll layout, provide a custom `PollContent` to `Channel`.

```tsx
import { ReactNode } from "react";
import { Channel } from "stream-chat-react";
import { CustomPollHeader, CustomPollOptionList } from "./Poll";

const PollContents = () => (
  <div>
    <CustomPollHeader />
    <CustomPollOptionList />
  </div>
);

const ChannelWrapper = ({ children }: { children: ReactNode }) => (
  <Channel PollContents={PollContents}>{children}</Channel>
);
```

## Poll API and state

To fully customize poll UI, use the Poll API and subscribe to poll state.

The Poll API is exposed via a `Poll` instance. It’s provided through React context to the children of the `Poll` component (rendered internally by `Message`). Access it via `usePollContext`:

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

const Component = () => {
  const { poll } = usePollContext();
};
```

The `Poll` instance exposes the following methods:

- `query` - queries the data for a given poll (permission to query polls is required)
- `update` - overwrites the poll data
- `partialUpdate` - overwrites only the given poll data
- `close` - marks the poll as closed (useful for custom `EndPollDialog`)
- `delete` - deletes the poll
- `createOption` - creates a new option for given poll (useful for custom `SuggestPollOptionForm`)
- `updateOption` - updates an option
- `deleteOption` - removes the option from a poll
- `castVote` - casts a vote to a given option (useful for custom `PollOptionSelector`)
- `removeVote` - removes a vote from a given option (useful for custom `PollOptionSelector`)
- `addAnswer` - adds an answer (comment)
- `removeAnswer` - removes an answer (comment)
- `queryAnswers` - queries and paginates answers (useful for custom `PollAnswerList`)
- `queryOptionVotes` - queries and paginates votes for a given option (useful for option detail)

Access poll state in custom components like this:

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

import type { PollState, PollVote } from "stream-chat";

type PollStateSelectorReturnValue = {
  latest_votes_by_option: Record<string, PollVote[]>;
};

// 1. Define the selector function that receives the fresh value every time the observed property changes
const pollStateSelector = (
  nextValue: PollState,
): PollStateSelectorReturnValue => ({
  latest_votes_by_option: nextValue.latest_votes_by_option,
});

const CustomComponent = () => {
  // 2. Retrieve the poll instance from the context
  const { poll } = usePollContext();
  // 3. Use the useStateStore hook to subscribe to updates in the poll state with selector picking out only properties we are interested in
  const { latest_votes_by_option } = useStateStore(
    poll.state,
    pollStateSelector,
  );
};
```

<admonition type="warning">

Don’t read `poll` data from `MessageContext.message`. It’s static seed data; use `poll.state` instead.

</admonition>

## PollContext

The context is available to all the children of the `Poll` component. Currently, it exposes the following properties:

### poll

The instance of a `Poll` class provided by the low-level client. The instance is retrieved from `PollManager` via `client.polls.fromState(pollId)`

```tsx
import { Poll, useChatContext, useMessageContext } from "stream-chat-react";

const Component = () => {
  const { client } = useChatContext();
  const { message } = useMessageContext();
  const poll = message.poll_id && client.polls.fromState(message.poll_id);

  if (!poll) return null;
  return <Poll poll={poll} />;
};
```

This extraction is done internally by the `MessageSimple` component.


---

This page was last updated at 2026-04-21T07:55:45.415Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v13/components/message-components/poll/](https://getstream.io/chat/docs/sdk/react/v13/components/message-components/poll/).