[Photographer Name] · Portfolio + Proofing
A photographer site. Public marketing galleries up top. Password-gated client proofing galleries underneath. One inquiry form.
Source of truth
Vercel auto-deploys. Public galleries are MDX in the repo (the photographer or you curate). Private client galleries live in Cloudinary folders, indexed in a small SQLite database tracking access codes + favorites.
Tech stack
Next.js 15 (App Router) + React + Tailwind v4. Cloudinary for image hosting, transformation (AVIF/WebP, responsive sizes), and the per-photo download URL. SQLite (via Turso) for client galleries + favorites. Resend for inquiry emails. Vercel hosting.
Deploy
git push origin main. Vercel + Cloudinary pull. New client gallery = upload to Cloudinary folder + add an entry to the client_galleries table.
File map
app/page.tsxhome with featured work, about, inquiry CTAapp/galleries/[slug]/page.tsxpublic marketing galleries (weddings, portraits, brands)app/clients/[code]/page.tsxpassword-gated client proofing galleryapp/clients/[code]/favorites/page.tsxshareable favorites viewapp/inquire/page.tsxinquiry formapp/api/inquire/route.tsPOST -> Resendapp/api/favorites/route.tstoggle favorite per-photodb/schema.sqlclient_galleries,client_favoritescomponents/Lightbox.tsxkeyboard-navigable, swipe on mobilecomponents/MasonryGrid.tsxresponsive Cloudinary-powered grid
.env keys
CLOUDINARY_CLOUD_NAME,CLOUDINARY_API_KEY,CLOUDINARY_API_SECRETTURSO_DATABASE_URL,TURSO_AUTH_TOKENRESEND_API_KEYRESEND_NOTIFY_EMAILphotographer's inboxCLIENT_GALLERY_BASE_URLfor links sent to clients
Hard rules
- Images are Cloudinary-hosted with
f_auto,q_autotransforms. Never serve a raw JPEG. - Responsive image sources:
srcSetincludes 400w, 800w, 1600w, 2400w. Mobile devices don't need 2400w. - Client galleries are HMAC-gated by access code in the URL. Anyone with the code sees the gallery. Authenticated favorites use a per-visitor cookie.
- Downloads are watermarked unless the client paid (track in the
client_galleriesrow). Watermarks via Cloudinary overlay. - Lightbox supports keyboard arrows + swipe. Slow lightboxes are a tell that you didn't actually shoot anything.
- Inquiry form fields are minimal: name, email, event date, type, message. Anything more loses leads.
Recent significant changes
- 2026-05-06: Scaffolded. Locked: Cloudinary over self-hosted (transforms + delivery), Turso for galleries (edge-fast, free tier generous), Resend for inquiries (DX).
Next session: start here
- Set up Cloudinary, create folder convention:
public/,clients/<code>/. - Create Turso DB. Apply
db/schema.sql. - Upload 30-50 of the photographer's best photos. Featured gallery goes live.
- Build one client gallery end-to-end with a real access code.
- Test inquiry form. Confirm Resend delivers to the photographer's actual inbox.