Skip to content
Sign in
Apps SDKPoll widgetMenu

Poll widgetPro

A poll that lives in the thread, with results that grow from each row as votes arrive and an owner close that names the winner.

ProA studio poll for the winter offsite with votes arriving live, closed by its owner.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/poll-widget

Usage

tsx
import { PollWidget } from "@/components/ui/chatcn/pro/poll-widget"

<PollWidget
  question="Where should the winter offsite be?"
  options={options}
  closesIn="Fri 18:00"
  onVote={(ids) => castVote(ids)}
  onClose={closePoll}
/>

Each option carries the votes from everyone else, and the viewer's own vote is added from vote, so an optimistic vote never double counts. Once someone has voted, or peeks with See results, the rows turn into their own results: a soft fill and a hairline grow from the start edge to each option's share, and they keep moving as new counts arrive. Percentages are whole numbers that always add up to 100.

With maxChoices above one the options become checkboxes and the rest disable at the cap. A vote whose promise rejects is taken back and the reason is shown under the footer. onClose adds Close poll for the owner; status="closed" locks the poll and names the winner, or the tie.

Render it inside the card your host draws for a widget answer. It fills the width it is given, so a comfortable max width (around 28 rem) comes from the card, and it follows the host theme through the kit tokens. Pass onExpand where the host can go full screen.

Examples

States

ProVoted, pick up to two, closed, a tie, a failed vote, long options, loading and an error.

Props

PropTypeNotes
questionstringWraps to two lines
options{ id, label, votes, detail? }[]Votes from everyone else
author, closesIn, anonymousThe meta line
maxChoicesnumberDefault 1
vote, defaultVotestring[] | nullThe viewer's vote
onVote(ids) => unknown | PromiseA rejection rolls the vote back
onClose() => unknown | PromiseOwner action
peekbooleanSee results before voting. Default true
status"open" | "closed" | "loading" | "error"Default open
voteErrorReactNodeA failure reported outside the promise
locale, onRetry, disabled
labelsPartial<…Labels>Every string the widget writes, for translation

Labels

labels covers the prompt, every button, the vote count (votes(n, count), where count is already formatted), the voted, won and tie lines (join builds the list of names), the closing time prefix and every error.

Keyboard

KeyAction
TabInto the options, then the footer buttons
Arrow keysMove between options in a single choice poll
SpacePick or drop an option

Accessibility

  • Options are native radios or checkboxes in a fieldset whose legend says how many to pick.
  • Results are an ordered list in a polite live region. Each row gives the percentage and the count as text; your own vote carries a named check, and the leader is marked by weight as well as the accent hairline.
  • The winner line uses an icon and words, never color alone.

Motion

Result fills scale from the start edge in 280 ms with the kit's ease out and follow new votes without restarting. Reduced motion jumps to the new share.