npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-poll-kit

v0.1.3

Published

A React Native UI kit for building WhatsApp-style interactive polls with clean chat-like design and real-time vote visualization.

Readme

react-native-poll-kit

React Native poll component and voting UI in the WhatsApp style: single- or multi-select polls, animated result bars, percent labels, vote counts, and per-option colors for chat, channels, and surveys. Built for iOS and Android with TypeScript types—no extra native modules.

Keywords: react native poll, whatsapp poll ui, vote component, survey widget, multi select poll, poll results bars, react native typescript, mobile chat poll.

npm License: MIT

Preview

| Animated demo | Still (results + multi-select) | |---------------|----------------------------------| | react-native-poll-kit demo — vote and results animation | react-native-poll-kit screenshot — colored options and WhatsApp-style bars |

Same assets in the repo: poll.gif, Screenshot_1778332631.png.

Features

  • Single tap vote or multi-select with a Vote button
  • Results mode: animated (optional) progress bars, %, checkmark, footer vote count
  • Theme-driven colors plus whatsappChatTheme preset (classic green #25D366)
  • i18n: override strings (voteButton, formatVoteCount) or use mergePollStrings
  • Layout animation when switching to results ( resultLayoutAnimation, default true; opt out if it clashes with navigation)
  • Minimal options: only id, label, votes required — text/radio/bars use theme when you skip textColor
  • Optional per-row textColor / radioColor / checkmarkColor / percentColor / backgroundColor / bar overrides
  • Theme keys radioColor, checkmarkColor, percentTextColor for global control without per-option noise
  • theme.optionRowBackgroundColor — shared row tint when options don’t set backgroundColor
  • allowRevote to reduce duplicate onVote while parent state updates
  • Layout: mainContainerStyle, style, and granular StyleProp hooks
  • Helpers: sumVotes, getProgressColor, formatPollPercent, mergePollTheme

Install

npm install react-native-poll-kit
# or
yarn add react-native-poll-kit

Peer dependencies: react, react-native (no extra native modules beyond core RN).

Public API

import {
  Poll,
  defaultPollStrings,
  defaultPollTheme,
  mergePollStrings,
  mergePollTheme,
  whatsappChatTheme,
  sumVotes,
  getProgressColor,
  formatPollPercent,
  type PollProps,
  type PollOption,
  type PollTheme,
  type PollStrings,
} from 'react-native-poll-kit';

Quick start

You own questions, options, vote counts, when to show results, and selectedOptionIds. After onVote, bump votes, set showResults, and pass the user’s selectedOptionIds.

When updating tallies, use { ...o, votes: next } so any optional fields (e.g. textColor) stay intact. If you pass new object literals for theme or strings every render, wrap them in useMemo so merges stay stable.

import { useCallback, useState } from 'react';
import { View } from 'react-native';
import { Poll, type PollOption, whatsappChatTheme } from 'react-native-poll-kit';

const initialOptions: PollOption[] = [
  { id: '1', label: 'Option A', votes: 0 },
  { id: '2', label: 'Option B', votes: 0 },
];

export function MyPoll() {
  const [options, setOptions] = useState(initialOptions);
  const [showResults, setShowResults] = useState(false);
  const [selected, setSelected] = useState<string[]>([]);

  const onVote = useCallback((ids: string[]) => {
    setSelected(ids);
    setOptions((prev) =>
      prev.map((o) =>
        ids.includes(o.id) ? { ...o, votes: (o.votes ?? 0) + 1 } : o
      )
    );
    setShowResults(true);
  }, []);

  return (
    <View style={{ padding: 16 }}>
      <Poll
        question="What should we build next?"
        options={options}
        allowMultiple={false}
        showResults={showResults}
        selectedOptionIds={selected}
        onVote={onVote}
        theme={whatsappChatTheme}
        allowRevote={false}
        mainContainerStyle={{ alignSelf: 'stretch' }}
        style={{ maxWidth: '100%', borderRadius: 16 }}
      />
    </View>
  );
}

Internationalization (strings)

Override voteButton (multi-select submit) and formatVoteCount(total) (footer under results). Defaults are English; defaultPollStrings and mergePollStrings match the theme pattern.

<Poll
  strings={{
    voteButton: 'Votar',
    formatVoteCount: (n) => (n === 1 ? '1 voto' : `${n} votos`),
  }}
  /* … */
/>

If theme or strings are built from literals, memoize them (same pattern as mergePollTheme):

import { useMemo } from 'react';
import { mergePollStrings, whatsappChatTheme } from 'react-native-poll-kit';

const theme = useMemo(
  () => ({ ...whatsappChatTheme, accentColor: '#128C7E' }),
  []
);
const strings = useMemo(
  () =>
    mergePollStrings({
      voteButton: 'Votar',
      formatVoteCount: (n) => (n === 1 ? '1 voto' : `${n} votos`),
    }),
  []
);

Colors quick reference

Theme (mergePollTheme / Poll theme prop) — set once for the whole poll:

| Key | What it controls | |-----|------------------| | cardBackground, cardBorderColor | Poll card surface | | questionColor | Question title | | optionTextColor | Option labels when a row has no textColor | | metaTextColor | Footer “N votes”, dim copy; default % color when nothing else applies | | accentColor | Multi-select Vote tint, progress fallback, radio when no radioColor / textColor | | radioColor | All voting circles (border + dot), unless a row sets radioColor or textColor | | checkmarkColor | All results ✓ marks, unless a row sets checkmarkColor or textColor | | percentTextColor | All results % numbers when a row omits percentColor / textColor | | voteButtonTextColor | Enabled Vote label | | progressTrack* | Result bar fills (see getProgressColor) |

Per option (PollOption) — override a single row:

| Key | What it controls | |-----|------------------| | textColor | Label; default source for radio / ✓ / % unless you set the specific keys below | | radioColor | Voting circle only (label can stay optionTextColor) | | checkmarkColor | ✓ in results only | | percentColor | % in results only | | backgroundColor | Row background | | progressColor* | Bar colors in results |

Defaults when option colors are omitted

| You skip… | Library uses… | |-----------|----------------| | textColor | theme.optionTextColor; % uses theme.percentTextColor ?? theme.metaTextColor | | radioColor | theme.radioColor ?? textColor ?? theme.accentColor | | checkmarkColor | theme.checkmarkColor ?? label color | | percentColor | textColor ?? theme.percentTextColor ?? theme.metaTextColor | | backgroundColor | Transparent, or theme.optionRowBackgroundColor if set on theme | | progressColor* | theme.progressTrackSelectedColor / progressTrackUnselectedColor (via getProgressColor) |

Shared row tint without per-option backgroundColor:

<Poll
  theme={{
    ...whatsappChatTheme,
    optionRowBackgroundColor: 'rgba(0, 0, 0, 0.04)',
  }}
  /* … */
/>

Multi-select

Set allowMultiple={true}. User picks options, then Vote; onVote receives all selected ids.

Live / server data

Pass new options (and optional totalVotes) whenever your REST API or WebSocket updates — the UI reflects new counts and percentages. showResults and selectedOptionIds usually come from your app state (after onVote or from a “my vote” endpoint), not from a static poll payload.

Backend / API payload (complete reference)

Use camelCase in JSON if your server matches TypeScript; many stacks use snake_case—map either way into PollOption (see Map API to PollOption).

Poll resource fields (typical GET /polls/:id or realtime message)

| Field | Type | Required | Maps to <Poll /> | |-------|------|----------|-------------------| | question | string | yes | question | | options | array | yes | options (each row → PollOption) | | allowMultiple / allow_multiple | boolean | no | allowMultiple (default single-select) | | totalVotes / total_votes | number | no | totalVotes (footer / totals; default = sum of option votes) | | selectedOptionIds / selected_option_ids | string[] | no | selectedOptionIds (current user’s choices; often from another endpoint) | | Per-option votes | number | for results | Each option’s tally when showResults |

Per-option color & bar fields (all optional; omit any key and the theme fills in—see Defaults when option colors are omitted):

| API / PollOption field | Purpose | |--------------------------|---------| | textColor | Label; default for radio / ✓ / % when specific keys omitted | | radioColor | Voting-mode circle only | | checkmarkColor | Results ✓ only | | percentColor | Results % only | | backgroundColor | Row background (voting + results track base) | | progressColor | Single bar color for this option (results) | | progressColorSelected | Bar fill when this option is in selectedOptionIds | | progressColorUnselected | Bar fill when the viewer did not pick this option |

Example A — Colored single-choice poll (like “lunch” demo)

Full PollOption styling so each row has its own tint and percent color; tallies match a live poll.

{
  "id": "poll_lunch_001",
  "question": "Where should we go for lunch?",
  "allowMultiple": false,
  "totalVotes": 48,
  "options": [
    {
      "id": "opt_italian",
      "label": "Italian",
      "votes": 18,
      "textColor": "#7B241C",
      "backgroundColor": "#FDEDEC",
      "progressColorSelected": "#C0392B",
      "progressColorUnselected": "#F5B7B1"
    },
    {
      "id": "opt_sushi",
      "label": "Sushi",
      "votes": 22,
      "textColor": "#1B4F72",
      "backgroundColor": "#EBF5FB",
      "progressColorSelected": "#2874A6",
      "progressColorUnselected": "#AED6F1"
    },
    {
      "id": "opt_salad",
      "label": "Salad bar",
      "votes": 8,
      "textColor": "#145A32",
      "backgroundColor": "#E9F7EF",
      "progressColorSelected": "#1E8449",
      "progressColorUnselected": "#A9DFBF"
    }
  ]
}

Example B — Multi-select, theme-driven colors (like “features” demo)

No per-row colors: the app uses whatsappChatTheme (or mergePollTheme) so accent, bars, and Vote match your chat brand.

{
  "id": "poll_features_002",
  "question": "Which features should we ship next? (pick all that apply)",
  "allowMultiple": true,
  "totalVotes": 127,
  "options": [
    { "id": "feat_dark", "label": "Dark mode", "votes": 89 },
    { "id": "feat_polls", "label": "Polls in channels", "votes": 54 },
    { "id": "feat_voice", "label": "Voice notes", "votes": 41 }
  ]
}

Example C — Minimal poll (API only sends ids, labels, votes)

{
  "question": "Ship dark mode?",
  "allowMultiple": false,
  "options": [
    { "id": "1", "label": "Yes", "votes": 41 },
    { "id": "2", "label": "No", "votes": 7 }
  ]
}

Example D — Same as A, snake_case (Rails / Django / Phoenix style)

{
  "question": "Where should we go for lunch?",
  "allow_multiple": false,
  "total_votes": 48,
  "options": [
    {
      "id": "opt_italian",
      "label": "Italian",
      "votes": 18,
      "text_color": "#7B241C",
      "background_color": "#FDEDEC",
      "progress_color_selected": "#C0392B",
      "progress_color_unselected": "#F5B7B1"
    },
    {
      "id": "opt_sushi",
      "label": "Sushi",
      "votes": 22,
      "text_color": "#1B4F72",
      "background_color": "#EBF5FB",
      "progress_color_selected": "#2874A6",
      "progress_color_unselected": "#AED6F1"
    },
    {
      "id": "opt_salad",
      "label": "Salad bar",
      "votes": 8,
      "text_color": "#145A32",
      "background_color": "#E9F7EF",
      "progress_color_selected": "#1E8449",
      "progress_color_unselected": "#A9DFBF"
    }
  ]
}

Map API to PollOption (TypeScript)

import { Poll, whatsappChatTheme, type PollOption } from 'react-native-poll-kit';

/** Supports camelCase or snake_case option rows from your API */
type ApiPollOption = {
  id: string;
  label: string;
  votes: number;
  textColor?: string;
  text_color?: string;
  radioColor?: string;
  radio_color?: string;
  checkmarkColor?: string;
  checkmark_color?: string;
  percentColor?: string;
  percent_color?: string;
  backgroundColor?: string;
  background_color?: string;
  progressColor?: string;
  progress_color?: string;
  progressColorSelected?: string;
  progress_color_selected?: string;
  progressColorUnselected?: string;
  progress_color_unselected?: string;
};

function toPollOption(o: ApiPollOption): PollOption {
  const textColor = o.textColor ?? o.text_color;
  const radioColor = o.radioColor ?? o.radio_color;
  const checkmarkColor = o.checkmarkColor ?? o.checkmark_color;
  const percentColor = o.percentColor ?? o.percent_color;
  const backgroundColor = o.backgroundColor ?? o.background_color;
  const progressColor = o.progressColor ?? o.progress_color;
  const progressColorSelected =
    o.progressColorSelected ?? o.progress_color_selected;
  const progressColorUnselected =
    o.progressColorUnselected ?? o.progress_color_unselected;

  return {
    id: o.id,
    label: o.label,
    votes: o.votes,
    ...(textColor != null && { textColor }),
    ...(radioColor != null && { radioColor }),
    ...(checkmarkColor != null && { checkmarkColor }),
    ...(percentColor != null && { percentColor }),
    ...(backgroundColor != null && { backgroundColor }),
    ...(progressColor != null && { progressColor }),
    ...(progressColorSelected != null && { progressColorSelected }),
    ...(progressColorUnselected != null && { progressColorUnselected }),
  };
}

type ApiPoll = {
  question: string;
  options: ApiPollOption[];
  allowMultiple?: boolean;
  allow_multiple?: boolean;
  totalVotes?: number;
  total_votes?: number;
};

function apiPollToProps(data: ApiPoll) {
  return {
    question: data.question,
    options: data.options.map(toPollOption),
    allowMultiple: data.allowMultiple ?? data.allow_multiple ?? false,
    totalVotes: data.totalVotes ?? data.total_votes,
  };
}

function PollFromPayload({
  apiResponse,
  showResults,
  selected,
  onVote,
}: {
  apiResponse: ApiPoll;
  showResults: boolean;
  selected: string[];
  onVote: (ids: string[]) => void;
}) {
  const pollProps = apiPollToProps(apiResponse);

  return (
    <Poll
      {...pollProps}
      showResults={showResults}
      selectedOptionIds={selected}
      onVote={onVote}
      theme={whatsappChatTheme}
    />
  );
}

For results math, each option should include votes when showResults is true. id and label are always required for each option.

Theming

| Export | Use | |--------|-----| | defaultPollTheme | Full default palette | | mergePollTheme(partial) | Build custom presets from partial overrides | | whatsappChatTheme | Drop-in green accent (partial PollTheme) |

<Poll theme={whatsappChatTheme} /* … */ />
<Poll theme={{ accentColor: '#128C7E' }} /* … */ />

Per-option styling (optional)

Use textColor, radioColor, checkmarkColor, percentColor, backgroundColor, and progressColor* when rows need different styles. Example: dark label text with a brand-colored ring — set radioColor: '#25D366' and textColor: '#111B21' on the same option.

Poll props (overview)

| Prop | Description | |------|-------------| | question | Title string | | options | PollOption[] — needs votes for result math when showResults | | showResults | Show bars, %, footer, results layout | | selectedOptionIds | Viewer’s choice(s) for highlight / checkmark | | allowMultiple | Multi-select + Vote vs single-tap | | onVote | (ids: string[]) => void | | allowRevote | false locks after submit until you reset (default true) | | totalVotes | Optional footer override (default: sum of option votes) | | theme | Partial<PollTheme> — includes optional optionRowBackgroundColor | | strings | Partial<PollStrings>voteButton, formatVoteCount (i18n) | | resultLayoutAnimation | LayoutAnimation when showResults becomes true (default true) | | animateProgress | Bar animation (default true) | | progressAnimationDuration | ms (default 1250) | | showSelectedCheckmark | ✓ next to % in results (default true) | | mainContainerStyle / containerStyle | Outermost wrapper (e.g. width) | | style | Card surface | | … | questionStyle, optionPressableStyle, separatorStyle, etc. — see PollProps in your IDE |

Utilities

import { sumVotes, getProgressColor, formatPollPercent, defaultPollTheme } from 'react-native-poll-kit';

Testing (this repo)

yarn test

Component tests use @testing-library/react-native (fireEvent, screen). When adding coverage, prefer querying by role or accessible text where Poll sets them.

Example app (this repo)

yarn install
yarn example ios
# or
yarn example android

Requirements

Match react / react-native to your app; the library targets standard RN without extra native SDKs.

License

MIT

Links