Overview
Feedback Fusion is a full-stack feedback management platform where users submit ideas, vote on features, and track progress through a public Kanban-style roadmap. It was built as an upgraded version of a reference implementation, with a focus on performance, type safety, and security hardening.
The project demonstrates how modern Next.js patterns — React Suspense streaming, Server Actions, and dual-layer authentication — can transform a standard CRUD app into a production-ready product tool.
Live Demo: nextjs-feedback-fusion.vercel.app Source Code: github.com/brian-dizon/nextjs-feedback-fusion
Problem It Solves
Product teams need a transparent way to collect and prioritize user feedback. Spreadsheets and email threads lose ideas; paid tools charge per seat for basic voting. Feedback Fusion provides a self-hosted alternative where the community drives the roadmap through democratic voting, and admins maintain control through a secure dashboard.
Architecture
The application uses Next.js 16 App Router with a clear separation between public pages, authenticated user flows, and protected admin routes.
Route Structure
feedback/— Community feedback board with category filtering and votingfeedback/new/— Submission form with client and server-side validationroadmap/— Public Kanban board showing feature status and progress statisticsadmin/— Secured admin dashboard for managing feedback statussign-in/,sign-up/— Custom themed Clerk authentication pagesapi/votes/— REST endpoint for toggle voting (requires auth)api/feedback/[id]/status/— Admin-only endpoint for status updates
Data Flow
- Authentication — Clerk handles OAuth and email/password login; a
syncCurrentUserhelper maps Clerk identities to the local database, creating users on first sign-in and auto-promoting the first user to admin - Streaming — Static headers and layouts render instantly; dynamic data streams in via React Suspense with skeleton fallbacks
- Mutations — Server Actions handle feedback submission with Zod validation; API routes handle votes and admin status updates
- Security — Double-lock: UI hides admin links for non-admins, and API routes independently verify the database role
Database Schema
PostgreSQL with Prisma ORM. Three core models:
- users — Mapped from Clerk identities with role (user/admin)
- posts — Feedback items with title, description, category, and status (under_review/planned/in_progress/completed)
- votes — Many-to-many join between users and posts with unique constraint preventing duplicate votes
Key Technical Decisions
Clerk over Better Auth
Clerk was chosen for its hosted authentication infrastructure, built-in OAuth providers, and seamless Next.js integration. The tradeoff is vendor dependency, but for a weekend build focused on feedback features rather than auth infrastructure, Clerk accelerated development significantly.
React Suspense Streaming
The original reference implementation blocked on database queries before rendering any UI. Feedback Fusion wraps all data-dependent components in Suspense boundaries, allowing headers, navigation, and static content to appear instantly while database queries resolve in parallel.
Server Actions for Forms
Feedback submission uses Next.js Server Actions instead of API routes. This eliminates client-side fetch boilerplate, provides automatic type safety, and enables progressive enhancement — forms work without JavaScript.
Double-Lock Authorization
Admin security is enforced at two levels: the UI conditionally renders admin navigation, and API routes independently check the database role. Bypassing the UI is insufficient; the server rejects unauthorized requests regardless of client state.
In-Memory Statistics
The roadmap page calculates vote counts, completion percentages, and category breakdowns in-memory from the fetched dataset rather than issuing separate aggregation queries. This reduces database round-trips by approximately 75% on the roadmap view.
What Was Built
Community Feedback Board
Users browse feedback by category, view vote counts, and submit new ideas. The category sidebar shows item counts and color-coded icons. Each feedback card displays the author, vote count, and current status.
Voting System
Authenticated users toggle votes on feedback items. The UI updates optimistically while the API persists the change. Duplicate votes are prevented at the database level via a unique composite index on (userId, postId).
Public Roadmap
A four-column Kanban board (Under Review, Planned, In Progress, Completed) with progress statistics, overall completion percentage, and vote-sorted cards within each column. Status badges indicate active work or shipped features.
Admin Dashboard
Role-gated table showing all feedback with status management. Admins update item status via dropdown, which triggers revalidation and updates the public roadmap instantly. Unauthorized access attempts are logged and redirected.
User Synchronization
A syncCurrentUser utility bridges Clerk and the local database. On first login, it creates a database user and auto-assigns admin role if they are the first user. Subsequent logins update name, email, and avatar from Clerk.
Metrics
| Metric | Value |
|---|---|
| Total LOC | 2,478 |
| Files | 57 |
| Languages | TypeScript, TSX |
| Database Models | 3 |
| UI Components | 12 shadcn/ui primitives |
Lessons Learned
Suspense boundaries must be granular. Wrapping an entire page in a single Suspense boundary defeats the purpose. Splitting static headers from dynamic content creates the perception of instant loading even when database queries take seconds.
Clerk’s currentUser() is server-only. Attempting to call it in client components silently fails. The sync pattern — server-side user creation with client-side ID reference — bridges the gap cleanly.
Database roles are the source of truth. Client-side role checks improve UX but cannot replace server verification. The double-lock pattern prevents privilege escalation even if the frontend is tampered with.
Server Actions simplify forms but complicate error handling. Unlike API routes where HTTP status codes communicate failure, Server Actions return serializable objects. A consistent { success, error } response shape keeps error handling predictable across the app.
Next Steps
- Email notifications — Notify users when their feedback changes status or reaches a vote threshold
- Comments — Threaded discussion on feedback items
- Search and filter — Full-text search across titles and descriptions
- Public API — REST endpoints for third-party integrations
- Webhook support — Trigger external services on status changes