
A multi-tenant memorial platform built sole-developer across 3 apps (public site, admin UI, and admin API) with multilingual support and white-label architecture.
Cherished Lives is a memorial platform built for families to keep the memories of loved ones alive: rich tribute pages, photo and video galleries, and a space for friends and family to share remembrances. The platform is production-ready across staging and main branches, built end to end by me as the sole developer across three independently deployed applications.
Cherished Lives
As the sole developer, I built Cherished Lives end to end across three separate applications: the public memorial site, an internal admin panel, and a dedicated admin API. My work covered authentication flows, profile management, media handling, access control, real-time updates, and the admin tooling used to manage users and content behind the scenes.

The problems that required the most thought while building this project.
Profiles can be fully public, private, or shared with specific people through invitations and access requests. Getting the permission logic right across API routes, middleware, and UI states without creating gaps was one of the harder parts of the build.
The platform supports white-label deployments where different brands share the same database. Every query touching users or profiles had to be scoped to the correct App ID. A missing filter meant data leaking across tenants, so I had to be consistent about this everywhere.
File uploads are handled by the admin backend, not the main app, because media processing (image optimization, video transcoding with FFmpeg, S3 uploads) belongs server-side. Coordinating upload flows between the two apps while keeping the user experience smooth required careful API design.
Between email/password, Google OAuth, email verification, password reset, and role-based access, the auth system had a lot of moving parts. Each flow needed to handle errors gracefully, redirect correctly, and keep the user informed at every step.
The platform is a monorepo with three independently deployed apps. The main app handles everything users see and interact with. The admin backend is a dedicated Express API for internal operations. Each app owns its own dependencies, and the shared MongoDB database is accessed through Prisma with strict per-tenant scoping.
apps/
main-app/ (Next.js 16, App Router - public site + dashboard)
app/
api/v1/ (REST API routes per resource)
auth/ (login, register, verify, reset)
dashboard/ (protected user area)
[tagid]/ (public memorial profile pages)
components/
auth/ (auth flow UI)
profile/ (profile display and editing)
media/ (gallery, upload, slideshow)
dashboard/ (dashboard-specific UI)
ui/ (Radix UI primitives, shadcn pattern)
lib/
auth/ (NextAuth config + helpers)
redux/api/ (13 RTK Query slices)
email/ (SendGrid templates)
prisma.ts (singleton client)
messages/ (en, es, fr, de translations)
admin-app/ (Next.js 16 - internal admin UI)
admin-backend/ (Express 5 - admin REST API + media processing)How I approached building this from first commit to deployment.
Designing the data model first
Before writing any UI, I worked out the Prisma schema in detail: 17 models covering profiles, media, tributes, access control, invitations, and tags. Getting this right early meant the API and UI work had a stable foundation to build on.
Building auth before anything else
Authentication and session management were the first things I completed. Registration, verification, login, password reset, and Google OAuth were all working end-to-end before any feature work started.
One feature slice at a time
Each feature was built fully (API route, RTK Query integration, UI components, loading states, error handling) before moving on. This kept the product stable and prevented half-finished states from piling up.
Admin tooling in parallel
The admin backend and admin UI were developed alongside the main app so internal operations like user management, profile moderation, and tag assignment were available as soon as the main features needed them.
The main modules I built end-to-end.
Memorial profiles
Rich profile pages with biography, life events, family tree, photo and video galleries, and a tribute wall for messages from friends and family.
Access control and privacy
Profiles can be public or private. Private profiles support manager invitations and access requests, giving owners full control over who can view or contribute.
Media and slideshows
Upload photos and videos, organize them into galleries, and create memorial slideshows with background audio, all processed and stored on DigitalOcean Spaces.
QR and NFC tag linking
Physical tags (QR codes or NFC) can be linked to a profile, letting anyone scan a memorial marker and land directly on the digital tribute page.
Real-time tributes and comments
Tributes, comments, and likes update in real time across all visitors using Pusher, making memorial pages feel alive and connected.
Multilingual support
The entire platform is available in English, Spanish, French, and German, with automatic locale detection based on the visitor's location.

What the project delivered.
3 apps
Built sole-developer across the public platform, admin UI, and admin API, all independently deployed
4 languages
Supported with automatic locale detection across the entire platform
Multi-tenant
White-label architecture supporting multiple brands from a single codebase