Why We Built It
For the first two years, we managed client communication through Notion, WhatsApp, Loom videos, and email threads. It worked — barely. Projects had 4–5 communication channels. Files lived everywhere. Approvals happened in WhatsApp voice notes.
The cost was real: project managers spent 30–40% of their time just maintaining alignment. Clients often didn't know what had been done or what was expected of them next.
The solution was obvious: a dedicated client portal. The implementation was not.
Technology Decisions
Framework: Next.js 15 App Router. We chose Next.js 15 because of the App Router's Server Components model, which lets us ship fast initial loads for the dashboard while keeping client-side interactivity where we need it. The streaming capability for dashboards with multiple data sources is genuinely useful.
UI: Custom glass design system. We deliberately chose not to use a component library like Shadcn or Radix for the primary UI. Our design direction — "Quiet Luxury Flow," dark mode, glassmorphism, Emerald Teal accent — is specific enough that off-the-shelf components fight the design rather than support it.
Database: PostgreSQL via Supabase. We considered Firebase, PlanetScale, and Turso. We chose Supabase for its excellent async Python support (our backend is FastAPI) and its production track record.
Backend: FastAPI. Python was the right choice because our AI integrations (Claude for content, Replicate for image upscaling, HeyGen for video) all have first-class Python SDKs.
The Design System
The design system has four primitives:
- Glass cards — rgba(255,255,255,0.03) background, subtle border, backdrop-filter: blur(12px)
- Teal accent — #00D4B8, used for active states, CTAs, and data highlights
- Indigo secondary — #6366F1, used for AI features and secondary actions
- Dark backgrounds — #0A0A0A base, #111111 surface, #1A1A1A elevated
The glass effect creates depth without adding visual noise. On a dark background, layered glass panels create hierarchy without requiring borders or shadows.
The Hardest Parts
Real-time without WebSockets. We initially planned WebSocket connections for live notifications and message updates. The infrastructure complexity wasn't worth it for our scale. We replaced it with Server-Sent Events (SSE) for the messaging feature and polling with React Query for everything else. Users notice no difference.
The approval workflow. Clients reviewing deliverables need to add comments, pin feedback to specific parts of a design, and approve or request revisions. Building this without a visual collaboration tool like Loom or Figma required us to build our own pin-based commenting system on top of preview iframes. It took three iterations to feel right.
Role-based access without complexity. Six user roles (Super Admin, Admin, Team, Client, Freelancer, Student) with different permissions for every feature. We solved this with a combination of server-side checks in the API and client-side rendering guards in the layout components.
What We'd Do Differently
Start with the client experience, not the admin experience. We built admin features first because they were easier to spec. The client-facing features — the ones that actually drive retention — came later and felt rushed in the first version.
Design the mobile experience in parallel. We designed desktop-first and adapted to mobile. Several components required significant rework to feel right on smaller screens. A mobile-first approach for the critical flows (project status, notifications, messaging) would have been faster overall.
The Result
Since launching the portal six months ago:
- Average WhatsApp messages per project: down 68%
- Client satisfaction scores: up 1.8 points (7.1 → 8.9 out of 10)
- Time spent on project admin by PMs: down 24%
- Client churn: down from 18% annual to 9%
The portal is now our primary retention tool. Clients who engage with it regularly renew at 2.3x the rate of clients who don't.