/library / template-vertical-photographer-portfolio
templateVertical (industry)

Photographer portfolio + client proofing galleries

A portfolio site for a working photographer. Public galleries for marketing, password-gated client proofing galleries with download + favorite, inquiry form. Built for someone who shoots, not someone who codes.

use whenYou're building for a wedding, portrait, or commercial photographer. Public galleries sell; private proofing galleries deliver. The CLAUDE.md captures both surfaces and the specific UX expectations.

May 6, 20262,150 bytesphotographyportfoliogalleryclient-portal

[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.tsx home with featured work, about, inquiry CTA
  • app/galleries/[slug]/page.tsx public marketing galleries (weddings, portraits, brands)
  • app/clients/[code]/page.tsx password-gated client proofing gallery
  • app/clients/[code]/favorites/page.tsx shareable favorites view
  • app/inquire/page.tsx inquiry form
  • app/api/inquire/route.ts POST -> Resend
  • app/api/favorites/route.ts toggle favorite per-photo
  • db/schema.sql client_galleries, client_favorites
  • components/Lightbox.tsx keyboard-navigable, swipe on mobile
  • components/MasonryGrid.tsx responsive Cloudinary-powered grid

.env keys

  • CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET
  • TURSO_DATABASE_URL, TURSO_AUTH_TOKEN
  • RESEND_API_KEY
  • RESEND_NOTIFY_EMAIL photographer's inbox
  • CLIENT_GALLERY_BASE_URL for links sent to clients

Hard rules

  • Images are Cloudinary-hosted with f_auto,q_auto transforms. Never serve a raw JPEG.
  • Responsive image sources: srcSet includes 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_galleries row). 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

  1. Set up Cloudinary, create folder convention: public/, clients/<code>/.
  2. Create Turso DB. Apply db/schema.sql.
  3. Upload 30-50 of the photographer's best photos. Featured gallery goes live.
  4. Build one client gallery end-to-end with a real access code.
  5. Test inquiry form. Confirm Resend delivers to the photographer's actual inbox.

Get the next CLAUDE.md in your inbox.

One new template every week, plus occasional case studies.