A real-time, peer-to-peer polling application built with modern web technologies. Create polls, share join links, and vote in real-time without relying on a central database or WebSocket server!
- ⚡️ P2P Real-time Updates: Uses WebRTC via
peerjsfor instant vote broadcasting from participants directly to the host. - 🚫 No Database Required: The host's browser acts as the source of truth for the poll session.
- 🔄 Cross-Tab Syncing: Leverages
zustandwith local storage persistence to act as a reliable fallback when PeerJS fails (especially useful for same-browser testing). - 🔗 Auto-Generating Join Links: Includes encoded poll data inside the join URL, so participants instantly see the question and choices without an initial server roundtrip.
- 🎨 Interactive UI: Built with
framer-motionfor smooth percentage bar animations andlucide-reactfor beautiful iconography. - 📊 Multiple Poll Types: Supports traditional Multiple Choice, Ranked Choice voting, dynamic Word Clouds, and live Q&A Boards with upvoting.
- 💅 Modern Styling: Styled with Tailwind CSS v4 and
clsx/tailwind-mergefor robust utility class management. - 🌈 Colour Scheme Switcher: Choose from four built-in themes — Light (default), Midnight, Vivid (orange/purple), and Ocean (teal/emerald). Selection is persisted in
localStorageand applied globally via a floating palette button.
You can configure additional integrations by creating a .env or .env.local file in the root directory:
| Variable | Description | Example |
|---|---|---|
NEXT_PUBLIC_PLAUSIBLE_DOMAIN |
The domain used for Plausible Analytics. | votify.example.com |
NEXT_PUBLIC_PLAUSIBLE_URL |
The URL to your Plausible script. | https://plausible.io/js/script.js |
NEXT_PUBLIC_BUYMEACOFFEE_SLUG |
Your creator slug for the "Buy Me A Coffee" widget. If provided, a floating button will appear on the bottom left. | RookieZA |
Votify ships with four built-in colour schemes. A floating palette button (bottom-right corner) lets you switch themes at any time — your selection is saved in localStorage so it persists across sessions.
| Theme | Accent colours | Description |
|---|---|---|
| ☀️ Light (default) | Indigo #6366f1 · Purple #a855f7 |
Clean, bright background with vibrant indigo/purple accents |
| 🌙 Midnight | Blue #3b82f6 · Violet #8b5cf6 |
Dark background with cool blue/violet accents |
| 🔥 Vivid | Orange #f26419 · Purple #7c3aed |
Dark background with punchy orange/purple contrast |
| 🌊 Ocean | Cyan #06b6d4 · Emerald #10b981 |
Dark background with refreshing teal/green tones |
The active theme is applied as a data-theme attribute on <html> (e.g. data-theme="midnight"), and all CSS variables are scoped to each theme in globals.css.
- Framework: Next.js 15+ (App Router)
- Language: TypeScript
- Styling: Tailwind CSS v4
- State Management: Zustand
- P2P Networking: PeerJS
- Icons: Lucide React
- Animations: Framer Motion
- QR Codes:
qrcode.react
src/app/page.tsx: The home page where hosts create a new poll.src/app/host/[id]/page.tsx: The host dashboard displays the QR code, connection status, and real-time voting results.src/app/join/page.tsx: The participant view. Decodes the poll from the URL and allows casting a single vote.src/hooks/usePeer.ts: Custom hook for the host to initialize a PeerJS instance and listen for incoming vote payloads.src/hooks/usePeerConnection.ts: Custom hook for participants to connect to the host's PeerJS instance and send votes.src/lib/store.ts: Zustand store for state management, includinglocalStoragepersistence and cross-tab synchronization.src/lib/themeContext.tsx: Theme context providing the active theme,setTheme, and the list of available themes.src/app/components/ThemeSwitcher.tsx: Floating palette UI component for switching colour schemes.src/app/components/ClientProviders.tsx: Client-side wrapper that injects the theme provider and switcher into the server layout.
- Node.js (v20+ recommended)
- npm, yarn, pnpm, or bun
-
Clone the repository and navigate to the project directory:
git clone https://github.com/RookieZA/Votify cd Votify -
Install the dependencies:
npm install
-
Run the development server:
npm run dev
-
Start Polling: Open http://localhost:3000 with your browser to create a poll.
- 🏠 Host Setup: When a host creates a poll,
usePeerinitializes a newPeerwith a unique ID. The poll data (question and choices) is saved to the local Zustand store. - 🔗 Join Link Generation: The host dashboard generates a join URL containing the host's Peer ID and a base64 encoded payload of the poll question and choices.
- 👤 Participant Join: When a participant opens the join link, the page decodes the URL to display the poll.
usePeerConnectionattempts to connect to the host's Peer ID. - 🗳️ Voting: Upon voting, the participant sends a
VOTEpayload over the WebRTC data channel. - 🛡️ Fallback Mechanism: To handle scenarios where WebRTC fails to connect (e.g., restricted networks or same-browser tab testing), joining participants also directly update the shared Zustand store. A
storageevent listener on the host side detects changes tolocalStorageand rehydrates the results immediately.


