A Scottish community organisation
Delivered — in handover
Community / non-profit · Next.js · Payload CMS · PostgreSQL · S3 · Resend · Playwright
The problem
The organisation's work — programmes, events, resources, community stories — was scattered across a site with no structure behind it, and every content change needed a developer. They needed a platform their own team could run.
What I did
I led the rebuild end to end: information architecture, the content model, the frontend, the CMS implementation, and the deployment and handover documentation.
Why a self-hosted CMS rather than a hosted one
The requirement that decided this was administrative, not editorial. The organisation needed a nine-role permission model, ownership of a submission queue, an audit log, soft delete, and personal-data access scoped by role. Hosted CMS platforms either put that tier of access control behind enterprise pricing or leave it out, which would have meant building a second admin application alongside the content one.
Payload gave me collection- and field-level access control, native drafts and scheduled publishing, and one deployable codebase sharing types with the frontend. I weighed it against Sanity, which was the original suggestion in the project brief.
I modelled fourteen collections rather than a generic page type, because each one has genuinely different access rules and publishing lifecycles. Treating an event, a policy document and a community story as the same object would have pushed that complexity into the editing experience, which is exactly where a small team cannot absorb it.
What was delivered
- Fourteen content collections with distinct access rules and draft/publish lifecycles
- Nine-role permission model with audit logging and soft delete
- WCAG 2.2 AA checks (Playwright + axe-core) running in CI alongside typecheck, lint and migration-backed builds
- Migration-driven database workflow and durable media storage
What I would do differently
I left the forms and email pipeline as non-submitting demos until late in the build, because the routing addresses and legal wording sat with the client. Wiring a stub adapter early and surfacing that dependency sooner would have shortened the handover tail considerably.