Why offline upload became the core of SnapUs

SnapUs is our own product, built and run by BDGroups. It is a real-time photo sharing platform for events: guests scan a QR code and every photo they take lands in one shared album. The idea is simple, but the reality of events is not. Venues are often crowded, signal is weak, and guests move between Wi-Fi and mobile data. If the app only worked with a stable connection, it would fail exactly when people want to use it most.

So we did not treat offline support as an edge case. We treated it as the product. SnapUs lets guests take photos with no signal, close the app, and have those photos upload automatically when connectivity returns. That reliability is what makes a shared album feel magical instead of frustrating.

The problem with naive photo upload

Most photo sharing apps assume a request will succeed. They fire an upload, wait for a response, and show an error if it fails. At a wedding with 300 guests, that means dozens of people staring at a spinner or retrying manually. Photos get lost, duplicates appear, and the album becomes a mess.

We wanted a different model: every photo is captured locally first, then uploaded in the background. The user never has to think about the network. The app does.

How the offline queue works

SnapUs is built with React Native using Expo, so we have one codebase for iOS and Android, plus a web build. The client stores each photo in a local persistent queue before it ever touches the network. That queue is not just an in-memory array; it survives app restarts and OS backgrounding.

When a photo is taken, we write the file to local storage, generate a content hash, and add a job to the queue. A background worker then processes jobs one at a time. If the upload succeeds, the job is removed. If it fails, the job stays and is retried later.

Retry with exponential backoff

We do not retry immediately in a tight loop. That would drain battery and hammer the server. Instead, we use exponential backoff with jitter. The first retry might be after a few seconds, then 30 seconds, then a few minutes. If the device is offline, we pause the queue entirely and listen for connectivity changes. When the network returns, processing resumes automatically.

Idempotent uploads

A retry can cause the same photo to be uploaded twice if the first request actually succeeded but the response was lost. To prevent duplicates, each upload job carries a content hash. The server checks that hash before storing the file. If a photo with the same hash already exists in the album, the server returns a success response without storing a duplicate. This makes retries safe.

Conflict handling: the part nobody talks about

Two guests can take nearly identical photos of the same moment. That is fine. But the same guest can also tap the shutter twice, or the OS can fire the capture callback twice. Filenames are useless for detecting this because they change. Content hashes are the only reliable signal.

We compute a SHA-256 hash of the image bytes on the device before upload. The server uses that hash as the deduplication key. If a duplicate arrives, we do not reject it; we acknowledge it and link the existing photo to the album. This keeps the client simple and the album clean.

What the server does

The SnapUs backend is a NestJS API. It handles authentication, album membership, upload sessions and deduplication. Media files go to S3, and CloudFront serves them to clients. The upload flow is deliberately boring: the client requests a signed upload URL, uploads the file directly to S3, then notifies the API with the content hash and metadata. The API validates the hash, checks for duplicates, and updates the album.

This separation means the API never has to handle large file bodies. It also means uploads can resume or retry without re-authenticating the whole flow. The signed URL is short-lived, but the queue can request a new one if needed.

Why this matters for event organisers

An event album is only useful if it is complete. If guests give up because uploads fail, the album is empty and the product is dead. Offline upload changes the psychology: guests take photos freely, knowing the app will handle the rest. Organisers see photos appear throughout the event, even from guests who had no signal for an hour.

This is not a feature we could have added later. It shaped the data model, the API design and the client architecture. We built SnapUs around the assumption that the network is unreliable, and that assumption has paid off in every real event we have run.

What makes SnapUs unique

There are plenty of photo sharing apps. Most are either social networks with albums bolted on, or event apps that require everyone to install something and sign in. SnapUs is different in three ways.

First, there is no app install required for guests. They scan a QR code and the web app opens. If they want the native experience, they can install the app, but it is not a barrier. Second, the album is live and shared by default. Every photo goes to one place, not to individual phones. Third, offline upload is built in from the start, not patched on. That combination means an event organiser can set up an album in minutes and trust that photos will actually arrive.

What we would do again

We would keep the local queue as the single source of truth for uploads. It simplified every other decision. We would also keep the content hash as the deduplication key from day one. Retrofitting that would have been painful. One thing we would improve is the connectivity detection on Android, where background restrictions vary by manufacturer. We now use a combination of network callbacks and periodic health checks, which is more reliable than relying on a single API.

Start a project with us

If you are building a product where reliability under poor connectivity matters, or you need a team that thinks through the hard parts before writing code, see what we build or check our starting prices. We would be happy to talk about your project.