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.
Prerequisites
- 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
Create the Poll, Option, and Vote data types
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.
Build the poll creation form
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.
Display the poll and voting buttons
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.
Record votes and update tallies
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.
Show live results with progress bars
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 working example
1VOTING AND POLLING — WORKFLOW SUMMARY2======================================34DATA TYPES:5 Poll: question (text), created_by (User), is_active (yes/no),6 end_date (date)7 PollOption: poll (Poll), option_text (text), vote_count (number, default 0)8 Vote: poll (Poll), option (PollOption), voter (User)910WORKFLOW: Create Poll11 Event: When "Create Poll" button is clicked12 Action 1: Create Poll → question, created_by, end_date13 Action 2-N: Create PollOption for each option input14 → poll = Result of step 1, option_text = input value1516WORKFLOW: Cast Vote17 Event: When "Vote" button is clicked18 Condition: Search for Vote (poll + voter) :count is 019 Action 1: Create Vote → poll, option = Current cell's PollOption, voter20 Action 2: Make changes to Current cell's PollOption21 → vote_count = vote_count + 12223DISPLAY:24 Repeating Group: Search for PollOption (poll = current poll)25 Each cell:26 - option_text (text)27 - Vote button (disabled when user already voted)28 - Progress bar (width = vote_count / total_votes × 100%)29 - Count + percentage text3031CONDITIONALS:32 Vote button disabled: when Vote search (poll + voter) count > 033 Results visible: when Vote search (poll + voter) count > 034 Poll expired: when Current date/time > Poll's end_date35 → hide vote buttons, show results onlyCommon mistakes when adding voting and polling features in Bubble.io: Step-by-Step Guide
Why it's a problem: Only checking for duplicate votes on the frontend
How to avoid: Add an Only when condition on the vote workflow action itself that checks the Vote search count is 0, enforcing the rule server-side.
Why it's a problem: Counting votes with a search instead of storing vote_count on options
How to avoid: Store and increment vote_count directly on the PollOption record for instant, WU-efficient display
Why it's a problem: Not handling expired polls
How to avoid: 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
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I want to build a polling feature in my Bubble.io app where users can create polls with multiple options, vote once per poll, and see live results as bar charts. Can you design the data model and outline the key workflows?
Add a voting and polling feature to my app. Create Poll, PollOption, and Vote data types. Build a poll creation form and a voting page with one-vote-per-user enforcement and live progress bar results.
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.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation