← Dev log

frontend

Three web apps, not one with role branching

Three web apps, not one with role branching

Not one app, three React 19 + Vite apps: web (public, people browsing), admin (organisation staff), super-admin (platform-wide operations). Each is a genuinely separate deployable app with its own routes, build and port, sharing code through workspace packages rather than one giant app with role-based branching.

A single app with if (role === "admin") scattered through it tends to become a tangle where every page accounts for every role, bundle size balloons with code most users never need, and a bug in admin-only logic risks the public experience. Separate apps mean web ships zero admin code, admin's bundle doesn't carry marketing pages, and a broken admin deploy doesn't touch web's uptime.

Frontend/
  apps/{web,admin,super-admin,mobile}/
  packages/
    types/     # shared TS types
    api/       # shared API client
    hooks/     # shared React hooks
    ui-web/    # shared shadcn/ui layer

The three apps import from the shared packages rather than each reimplementing its own client. That's what keeps a backend API change from silently drifting out of sync across three separately maintained frontends.

The two traps

Build order. The apps depend on the packages, so those have to build first or the apps import stale or missing compiled output.

pnpm --filter @app/types build
pnpm --filter @app/api build
pnpm --filter @app/hooks build
pnpm build

This exact sequence is in CI explicitly — getting it wrong shows up as confusing "type not found" or stale-behaviour bugs unrelated to whatever was actually just changed.

The local-dev version of the same trap: editing a shared package and not rebuilding it means every app importing it keeps running the old compiled output. A real fix looks invisible in every app until the package gets its own build step — the source file is correct, behaviour doesn't change, and it's genuinely not obvious why until you remember.

Each app also needs its own base-URL env var pointing at the right backend. With three separately deployed frontends, a misconfigured var in just one shows up as "admin can't reach the API" while web works fine — confusing unless per-app env config is the first thing you check.