← Back to Blog
nextjssupabasereactengineering

Engineering a Frictionless Wedding Photo Collector

April 28, 2026 · 4 min read

Cathy and I have been best friends for over a decade. When she and Dylan started planning their wedding, I was thrilled for them—and immediately started thinking about how I could contribute my engineering background to the big day.

They hit a common snag: how do you collect all the amazing, candid photos guests take on their phones?

Third-party solutions were either too rigid, compressed the image quality to death, or—worst of all—required guests to download a specific app and create an account. For a seamless guest experience, I wanted to build a frictionless, QR-code-to-upload flow that just worked on any device while preserving pristine image quality (including raw iPhone .heic files).

Here is a look at the technical decisions that went into building their custom photo collector.

The Architecture: Robust and Serverless

I needed an architecture that was robust, serverless, and capable of handling high bandwidth spikes without breaking the bank.

I chose Next.js 14 (App Router) for the fast React foundation and server-side APIs, paired with Supabase. Supabase was the MVP of the stack. I used Supabase Postgres to track submissions and Supabase Storage to hold the raw images. The generous free tier meant I could handle gigabytes of data at zero cost. For deployment and edge capabilities, I used Vercel.

Direct-to-Storage Uploads

The biggest mistake most photo platforms make is client-side compression. When a user uploads a photo, the browser compresses it to a JPEG to save bandwidth. I explicitly wanted to avoid this.

Instead, my UploadForm component interacts directly with the supabaseBrowser client. It takes the raw file straight from the iOS camera roll and streams it directly to the private wedding-photos Supabase bucket. The heavy lifting of conversion is passed on to the admins when they download the batch later.

The botanical-inspired upload form for guests

Engineering for Security at the Edge

Since the bucket is open for INSERT operations (via Row Level Security), I had to prevent abuse. If someone found the URL, they could theoretically spam the bucket with garbage.

I utilized Next.js Middleware (src/middleware.ts) to intercept the final database INSERT call at the edge:

  1. ·Event Window Lock: The app is configured with NEXT_PUBLIC_EVENT_START and END dates. If a request comes in outside of the wedding weekend, the middleware blocks it with a 403 Forbidden before it ever hits the database.
  2. ·Cookie-Based Rate Limiting: I chose HTTP-only cookies over IP-based rate limiting to avoid inadvertently blocking multiple guests sharing the venue's Wi-Fi.

Chunked ZIP Downloads

On the admin side, pulling down potentially gigabytes of high-res photos via a single ZIP file can easily crash the browser. I utilized jszip and file-saver to dynamically chunk downloads into batches of 50 photos directly on the client, ensuring the couple can reliably download every single memory.

The admin dashboard showing guest submissions and cat photos

The Botanical Design System

A wedding app shouldn't look like a generic SaaS product. Cathy and Dylan requested a "natural, botanical elegance" look.

I stripped out the system defaults and implemented Amarante for elegant, uppercase headings, and Baskervville for the body text via next/font/google. I defined a bespoke palette of Olive Green, Forest Green, Terra Cotta, and Blush Pink. To preserve the specific off-white "paper-like" background, I explicitly stripped out all dark mode support, guaranteeing visual integrity across all devices.

To tie it all together, I used the qrcode library to generate a high-resolution, branded QR code and built a print-ready HTML template (table-sign.html). This allowed the couple to print 5x7 signs directly from their browser with vector-sharp typography and a scannable code.

Ensuring "Big Day" Readiness

You can't test a wedding app on the actual day. It has to be perfect beforehand.

I moved beyond basic linting and implemented a robust automated suite using Vitest for unit-testing the core helper logic, and Playwright for end-to-end browser tests across Desktop Chromium, Mobile Chrome, and Mobile Safari. This ensured the responsive design held up and that edge cases were properly handled.

Building this collector was an incredibly rewarding way to use my engineering skills to celebrate Cathy and Dylan. It required piecing together storage, edge security, and frontend polish into a frictionless, zero-maintenance system that just worked exactly when it needed to.