Skip to content
All projects
LiveStill in active developmentSolo developer, personal productJun 2026 – present

SyncLancer

A self-initiated multi-tenant project management app for freelancers: leads, clients, proposals, projects, tasks, invoices, and time tracking in one place, with a portal clients can log into.

SyncLancer
Next.jsReactTypeScriptTailwind CSSshadcn/uiPostgreSQLSupabasePrisma ORMAuth.jsTanStack Query

The problem

Freelancers typically run their business across a CRM, a chat app, a file drive, an invoicing tool, and a spreadsheet. Nothing is linked, so the same client exists five times. I wanted to see whether one schema could hold the whole workflow without becoming unusable.

What I set out to build

Build a single workspace that covers a freelancer's business end to end, and use it as a way to learn tenant isolation, role-scoped access, and optimistic UI properly rather than in theory.

Approach

A multi-tenant Next.js app with a workspace boundary on every query. A drag-and-drop lead pipeline converts leads into clients, projects break into milestones and tasks, time entries roll up per project, and invoices render to PDF in the browser. Clients see a filtered view of the same data through a separate portal role.

Overview

SyncLancer is my own product, not client work. I built it to find out what multi-tenancy actually involves. Each freelancer gets an isolated workspace covering a lead pipeline, clients, proposals, projects, milestones, tasks, invoices, time tracking, and files. Clients get a separate scoped login where they can see their own milestones, download files, and view invoices.

Architecture

Next.js App Router with React on the frontend. Postgres on Supabase accessed through Prisma, with a workspace foreign key on every tenant-scoped table. Auth.js handles authentication and distinguishes owner from client-portal sessions. TanStack Query manages server state and optimistic updates, @dnd-kit powers the pipeline board, @react-pdf/renderer generates invoices client-side, Resend sends transactional email, and files live in Supabase Storage.

What shipped

  • Live at synclancer.web.id with the lead pipeline, projects, milestones, tasks, invoicing, and time tracking working end to end. I am still adding to it.
  • Client portal ships with scoped roles and an invitation flow, so a client only ever loads their own workspace's rows.
  • Invoices are generated as PDFs in the browser, which kept the server out of document rendering entirely.

Hard parts & trade-offs

  • Tenant isolation is only as good as its weakest query. Enforcing it per-call was fragile, so scoping moved into a shared query layer that refuses to build a query without a workspace id.
  • Optimistic updates on the Kanban board needed rollback that survives a failed reorder mid-drag. The naive version left the card in the wrong column after an error.
  • The client portal shares tables with the owner view, so every field had to be classified as client-visible or not. That classification, not the UI, was the actual work.

What I learned

  • Multi-tenancy is a data-access problem, not a feature. Deciding where the boundary is enforced is the whole design.
  • TanStack Query's optimistic updates are excellent right up to the point where you need to unwind a failure. The rollback path deserves as much attention as the happy path.
  • I scoped this far too wide for a solo project. A narrower version, shipped sooner, would have taught me the same things earlier.

What's next

  • 1Get it in front of actual freelancers. Right now the only user is me, so nothing about the workflow has been tested against reality.
  • 2Add payment collection so invoices can be settled in the app.
  • 3Write integration tests around the tenant boundary, which is currently only verified by hand.