A photo delivery and client-selection tool for photographers. Clients get a unique, PIN-protected link to browse their event photos and choose which ones they want delivered — digital files, album prints, and album cover — while photographers manage everything from a private admin dashboard.
Built solo, end-to-end: schema design, auth, file storage, image processing, and transactional email.
| Admin dashboard | Client selection view |
|---|---|
![]() |
![]() |
Photographers currently manage client photo selections over WhatsApp, email threads, or shared drive folders — slow, error-prone, and impossible to enforce package limits (e.g. "choose 30 digital photos, 15 for the album, 2 for the cover"). Picselectr replaces that with a single shareable link and automatic tier limits, cascading deselection, and a completion email the moment a client finishes.
- Unique selection link — Each event gets a shareable
/select/[slug]URL protected by a PIN - Three-tier photo selection with enforced limits per event — Digital → Album → Cover, each a subset of the last
- Cascading rules — Removing a digital photo automatically removes it from album and cover, keeping selections consistent
- Full-screen preview modal with selection controls
- Mobile-first responsive grid, built for clients selecting from their phone
- Event management — Create, view, and delete events (wedding, birthday, quinceañera, photobooth, etc.)
- Photo uploads directly from the dashboard, with automatic thumbnail generation
- Live selection review — See exactly what a client picked, per tier, as they pick it
- One-click shareable links with PIN protection
- Deadline tracking per event
- Automatic email notification the moment a client completes their selection, listing every photo chosen per tier
Client Browser Admin (photographer)
│ │
▼ ▼
┌─────────────────────────────────────────────┐
│ Next.js App Router │
│ /select/[slug] /events/[slug] │
│ (public, PIN-gated) (auth-protected) │
└───────────────┬───────────────┬───────────────┘
│ │
┌───────▼──────┐ ┌──────▼───────┐
│ Supabase │ │ Cloudflare │
│ Postgres+RLS │ │ R2 (photos) │
│ + Auth │ │ via sharp │
└───────┬───────┘ │ (thumbnails) │
│ └──────────────┘
┌───────▼───────┐
│ Resend API │
│ (email on │
│ completion) │
└───────────────┘
- Auth & session refresh handled in
middleware.tsvia Supabase SSR cookies — every request to/events/*is gated server-side, no client-side auth flicker. - Storage: original + thumbnail variants are generated with
sharpon upload (app/api/upload/route.ts) and streamed to Cloudflare R2 via the S3-compatible SDK (lib/r2.ts) — no vendor lock-in to AWS. - Data access: selection state and event data are read/written through Supabase with row-level security, so client-facing PIN routes and the authenticated admin dashboard share one Postgres instance safely.
- Notifications: completion emails are fire-and-forget (a failed send never blocks a client's save) and built with a plain
fetchcall to Resend's API — no SDK dependency for a single endpoint (app/events/store.ts).
| Layer | Choice |
|---|---|
| Framework | Next.js 16 (App Router, Server Actions) |
| Language | TypeScript |
| UI | React 19, Tailwind CSS v4 |
| Database/Auth | Supabase (Postgres, Row-Level Security, Auth) |
| File storage | Cloudflare R2 (S3-compatible, via @aws-sdk/client-s3) |
| Image processing | sharp (server-side thumbnailing) |
| Resend | |
| Package manager | pnpm |
pnpm install
cp .env.example .env.local # fill in Supabase, R2, and Resend credentials
pnpm dev| Variable | Purpose |
|---|---|
NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase project connection |
SUPABASE_SERVICE_ROLE_KEY |
Server-side privileged Supabase access (uploads, admin lookups) |
CLOUDFLARE_R2_ENDPOINT / CLOUDFLARE_R2_ACCESS_KEY_ID / CLOUDFLARE_R2_SECRET_ACCESS_KEY / CLOUDFLARE_R2_BUCKET_NAME / R2_PUBLIC_URL |
Photo storage |
RESEND_API_KEY / RESEND_FROM_EMAIL |
Completion email notifications (optional — silently skipped if unset) |
pnpm lint # ESLint
pnpm build # Production build- Automated test coverage (unit + e2e)
- Bulk photo download for photographers
- Multi-language support (currently Spanish-first copy)
Built by Ernesto Aviles — feel free to reach out at ernesto-av@hotmail.com.

