# How to add voting and polling features in Bubble.io: Step-by-Step Guide

- Tool: Bubble
- Difficulty: Beginner
- Time required: 20-25 min
- Compatibility: All Bubble plans
- Last updated: March 2026

## TL;DR

Build a voting and polling system in Bubble by creating a Poll data type with related Options, enforcing one-vote-per-user with a Votes data type, tallying results in real time, and displaying them as bar charts. This tutorial covers poll creation, vote recording, duplicate prevention, and live result visualization.

## Overview: Voting and Polling in Bubble

Polls and voting features let your users weigh in on questions, rank options, and see community sentiment. This tutorial builds a complete polling system in Bubble with multiple-choice options, one-vote-per-user enforcement, real-time result counting, and visual result display. It is ideal for community apps, feedback systems, and decision-making platforms.

## Before you start

- A Bubble account with user authentication set up
- Basic understanding of Data Types, Workflows, and Repeating Groups
- Familiarity with conditional formatting

## Step-by-step guide

### 1. Create the Poll, Option, and Vote data types

Go to Data tab and create three Data Types. 'Poll': question (text), created_by (User), is_active (yes/no, default yes), end_date (date). 'PollOption': poll (Poll), option_text (text), vote_count (number, default 0). 'Vote': poll (Poll), option (PollOption), voter (User). The Vote type links a user to their chosen option, enabling duplicate prevention.

> Pro tip: Storing vote_count directly on PollOption avoids counting searches later and gives instant results.

**Expected result:** Three Data Types are created with the specified fields and relationships.

### 2. Build the poll creation form

Create a page or popup for creating polls. Add an Input for the poll question, a Repeating Group with Inputs for option text (start with 3-4 rows), and a Date picker for the end date. Add a 'Create Poll' button with this workflow: Create a new Poll → question = Input's value, created_by = Current User, end_date = Date picker's value. Then for each option input: Create a new PollOption → poll = Result of step 1, option_text = input value. Use the Repeating Group's List of Inputs to iterate.

**Expected result:** Users can create a poll with a question, multiple options, and an end date.

### 3. Display the poll and voting buttons

On your poll display page, show the poll question in a Text element. Add a Repeating Group with data source: Do a search for PollOption (constraint: poll = Current page's Poll). In each cell, display the option_text and a 'Vote' button. The Vote button should be conditionally disabled when: Do a search for Vote (constraint: poll = Current page's Poll, voter = Current User) :count > 0. This prevents double voting.

**Expected result:** The poll displays with clickable vote buttons that become disabled after the user has voted.

### 4. Record votes and update tallies

On the Vote button workflow: Action 1: Create a new Vote → poll = Current page's Poll, option = Current cell's PollOption, voter = Current User. Action 2: Make changes to Current cell's PollOption → vote_count = Current cell's PollOption's vote_count + 1. Add an 'Only when' condition on the workflow: Do a search for Vote (poll = Current page's Poll, voter = Current User) :count is 0. This is a server-side guard against duplicate votes.

**Expected result:** Clicking a vote button creates a Vote record and increments the option's vote_count by 1.

### 5. Show live results with progress bars

Below each option in the Repeating Group, add a Group styled as a progress bar. Set its width dynamically: Current cell's PollOption's vote_count / Current page's Poll's PollOptions:each item's vote_count:sum × 100 (as a percentage). Set the background color to your brand color. Next to it, display the vote count and percentage as text. Add a conditional to show results either always or only after the user has voted.

> Pro tip: Use conditional visibility to hide results until the user votes — this prevents anchoring bias where users follow the leading option.

**Expected result:** After voting, users see a bar chart of results showing each option's percentage and count.

## Complete code example

File: `Workflow summary`

```text
VOTING AND POLLING — WORKFLOW SUMMARY
======================================

DATA TYPES:
  Poll: question (text), created_by (User), is_active (yes/no),
        end_date (date)
  PollOption: poll (Poll), option_text (text), vote_count (number, default 0)
  Vote: poll (Poll), option (PollOption), voter (User)

WORKFLOW: Create Poll
  Event: When "Create Poll" button is clicked
  Action 1: Create Poll → question, created_by, end_date
  Action 2-N: Create PollOption for each option input
    → poll = Result of step 1, option_text = input value

WORKFLOW: Cast Vote
  Event: When "Vote" button is clicked
  Condition: Search for Vote (poll + voter) :count is 0
  Action 1: Create Vote → poll, option = Current cell's PollOption, voter
  Action 2: Make changes to Current cell's PollOption
    → vote_count = vote_count + 1

DISPLAY:
  Repeating Group: Search for PollOption (poll = current poll)
  Each cell:
    - option_text (text)
    - Vote button (disabled when user already voted)
    - Progress bar (width = vote_count / total_votes × 100%)
    - Count + percentage text

CONDITIONALS:
  Vote button disabled: when Vote search (poll + voter) count > 0
  Results visible: when Vote search (poll + voter) count > 0
  Poll expired: when Current date/time > Poll's end_date
    → hide vote buttons, show results only
```

## Common mistakes

- **Only checking for duplicate votes on the frontend** — Frontend conditions can be bypassed. A user could vote twice if the page loads slowly or they use multiple tabs. Fix: Add an Only when condition on the vote workflow action itself that checks the Vote search count is 0, enforcing the rule server-side.
- **Counting votes with a search instead of storing vote_count on options** — A search for Vote :count runs on every page load for every option, consuming workload units quickly Fix: Store and increment vote_count directly on the PollOption record for instant, WU-efficient display
- **Not handling expired polls** — Users can still vote after the end_date if there is no check Fix: Add an Only when condition: Current date/time < Current page's Poll's end_date on the vote workflow

## Best practices

- Store vote_count directly on PollOption records to avoid expensive count searches
- Enforce one-vote-per-user both in UI (disabled button) and workflow (Only when condition)
- Hide results until the user has voted to prevent anchoring bias
- Add an end_date to polls and disable voting when expired
- Use privacy rules on the Vote type to prevent users from seeing who voted for what
- Add a total_votes field on Poll and update it with each vote for quick total display
- Allow poll creators to close polls early by toggling is_active to no

## Frequently asked questions

### Can I allow anonymous voting without user accounts?

Not reliably. Without user accounts, you cannot enforce one-vote-per-person. You could use browser cookies or IP tracking, but these are easily circumvented. User authentication is recommended for fair voting.

### How do I show results as a pie chart instead of bars?

Install the Chart.js plugin from the Plugins tab. Add a Pie chart element and set its data source to the PollOptions search. Map option_text to labels and vote_count to values.

### Can users change their vote?

Yes, with additional logic. When a user clicks a new option: delete their existing Vote record, decrement the old option's vote_count, create a new Vote, and increment the new option's vote_count.

### Will this work with thousands of voters?

Yes. Since vote_count is stored directly on PollOption, displaying results is a single search regardless of voter count. The individual Vote records exist for audit purposes but are not needed for display.

### How do I embed a poll on another page?

Create the poll display as a Reusable Element. Give it a data source of type Poll. Place it on any page and set the data source to the specific poll you want to show.

### Can RapidDev build a custom polling platform?

Yes. RapidDev can build advanced polling systems with features like ranked-choice voting, weighted polls, real-time WebSocket updates, and detailed analytics dashboards.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/add-voting-and-polling-features-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/add-voting-and-polling-features-in-bubble
