Case Study

Building Preloved
A Marketplace From Scratch

How I designed and built a production-ready second-hand marketplace with role-based dashboards, real-time chat, WCAG 2.1 AA accessibility, and 139 shipped user stories.

Role Full-Stack Developer
Stack React 19 · Firebase · Node.js
Type Academic + Personal Project
Status Live · 89% Complete
🕑 4 min read
139
User Stories
89%
Complete
AA
WCAG 2.1
3
User Roles

What is Preloved?

Preloved is a full-stack marketplace web application for buying and selling second-hand goods. It was built as an academic project that I pushed well beyond the brief — tracking 156 user stories across 6 feature categories, all shipped to 100% completion in their respective epics.

The platform supports three distinct user types: buyers who browse and purchase listings, sellers who manage inventory and fulfil orders, and administrators who oversee the platform and access analytics dashboards. Each role has its own tailored interface, access controls, and feature set.

The goal wasn't just to pass the assignment. I wanted to build something I'd be comfortable showing to a hiring manager as evidence of what I can ship — a real, live, working product with real users and real data.

Why build a marketplace?

Marketplaces are one of the most technically demanding types of web application. They require you to solve hard problems simultaneously: authentication, authorisation, real-time data sync, complex state management, file uploads, and multi-sided user experiences — all in one codebase.

Most student projects stop at a CRUD app with one user type and a simple database. I wanted to deliberately take on a project that would force me to make real architectural decisions: how to handle role-based access without leaking data, how to build a real-time messaging system that scales, and how to ship an accessible UI that works for everyone.

Why React + Firebase?

Every technology choice had a reason behind it:

React 19
Component model maps well to a marketplace's many reusable UI pieces — listing cards, user badges, dashboards. Concurrent features in React 19 made transitions smoother.
Firebase Firestore
Real-time listeners eliminate polling for chat notifications and inventory updates. Free tier is generous enough to run the live demo indefinitely.
Firebase Auth
Handles session persistence, email verification, and password reset out of the box — removing significant security surface area from my own code.
TailwindCSS
Utility-first approach kept the component styles co-located and eliminated the stylesheet naming problem at scale. Dark mode was trivial to implement.
Firebase Hosting
One command deploy (firebase deploy), global CDN, automatic HTTPS. The live demo has zero ongoing cost.
Agile / Kanban
Tracked all 156 user stories on a Kanban board with backlog, in-progress, and done columns. Gave visibility into completion and helped prioritise scope.

What I actually shipped

🔐
Role-based auth and access control
Firebase Auth with Firestore-stored roles. Buyers, sellers, and admins each see a different dashboard and have different Firestore security rules applied at the database level — not just the UI.
💬
Real-time chat with unread notifications
Firestore real-time listeners power the messaging system. Unread counts update instantly without polling. Read receipts mark messages as seen when the recipient opens the thread.
📦
Full seller inventory management
Sellers can create, edit, and delete listings with image uploads. Status management handles active, sold, and draft states. Order tracking shows the full buyer journey per listing.
📊
Admin analytics dashboard
Admins see platform-wide metrics: total listings, active users, order volumes, and category breakdowns. Built with aggregated Firestore queries — no external analytics service required.
WCAG 2.1 Level AA accessibility
Full keyboard navigation, ARIA labels on all interactive elements, screen reader tested, and colour contrast ratios verified throughout. Accessibility was built in from the start, not retrofitted.
🔗
Social sharing and referral system
Listings can be shared to 7 platforms. A referral system tracks sign-ups via shared links. An affiliate program rewards users for driving new registrations — all tracked in Firestore.

What was genuinely hard

Firestore security rules for multi-role access

The hardest single problem was writing Firestore security rules that allowed buyers to read listings, sellers to write only their own listings, and admins to read everything — without leaking data between roles.

Solution: Role stored as a Firestore field, read in security rules via get(). Each collection has explicit read/write rules checked against the authenticated user's role document. Tested manually across all three role types before deployment.

Real-time chat at scale without blowing the free tier

Naive Firestore listeners on every conversation would exhaust the free read quota within days on an active platform. Getting real-time updates while minimising reads required careful query design.

Solution: Listeners attach only to conversations the current user participates in. Pagination limits the initial message load. Unread counts are stored as a separate counter field, avoiding a full message read on every notification check.

Building accessible components from scratch

Most tutorials show how to build UI that looks accessible but breaks completely for keyboard users or screen readers. Modal dialogs, dropdown menus, and toast notifications all have specific ARIA patterns that took research to get right.

Solution: Read the WAI-ARIA Authoring Practices Guide for each component type. Implemented focus trapping in modals, roving tabindex in menus, and live regions for toast announcements. Tested with VoiceOver on macOS and NVDA on Windows.

Scope management across 156 user stories

At 156 user stories, it became genuinely difficult to decide what to build next and to avoid scope creep on features that were already "done enough".

Solution: Treated the Kanban board as the single source of truth. Prioritised P1 stories first in each epic before touching P2. Set a rule: no new stories added to an epic already at 100% — any new ideas went into a backlog for v2.

What was delivered

User stories shipped139 / 156 (89%)
Feature categories at 100%6 / 6
WCAG 2.1 AA complianceFull
🚀
Live and deployed
Running on Firebase Hosting with global CDN, automatic HTTPS, and zero ongoing cost on the free tier.
Fully accessible
WCAG 2.1 AA compliant — keyboard navigable, screen reader tested, and contrast verified throughout.
👥
Three user roles
Buyer, seller, and admin — each with their own dashboard, data access rules, and feature set.
Real-time data
Chat, notifications, and inventory updates powered by Firestore real-time listeners — no polling.

Key takeaways

Security at the data layer

UI-level access control is not security. Firestore rules enforce access at the database level regardless of what the frontend does. This is the correct model.

Accessibility is architecture

Bolting accessibility on after the fact is expensive and incomplete. Building it in from the start — focus management, ARIA, keyboard nav — is faster and more robust.

Scope discipline matters

A backlog without prioritisation becomes feature debt. Tracking 156 user stories forced me to make explicit prioritisation decisions rather than building whatever was interesting.

Firebase free tier is genuinely useful

With careful query design, a real marketplace can run on the Firestore free tier indefinitely. Understanding read/write costs shaped every data access pattern I built.

See it in action

The live demo is fully functional — you can sign up as a buyer or seller and explore the platform.