Niall Eccles
Back to blog

Why I Built a Desktop Photo App in 2025 (and Why Electron)

How a travel photography problem led me to build a desktop app with Electron, React, and Sharp

19 March 2026
Electron React TypeScript Side Projects

I take a lot of photos when I travel. Not professionally, but enough that coming home with 600 large sized files from a one week trip is normal for me. The problem isn’t taking the photos. It’s dealing with them afterwards.

The workflow I actually want is simple: open a folder, move through every shot quickly, discard the duplicates and bad shots, keep the ones worth editing. That’s it. But finding a tool that does exactly that, and nothing more, is surprisingly hard.

The problem with existing tools

Most photo management apps are built around libraries and catalogues. They want to import your photos, organise them their way, and keep you inside their ecosystem. That’s fine if you’re a professional with a structured workflow. For me, it’s something I don’t want.

Cloud-based tools have a different problem: I’m often travelling without a reliable internet connection. Uploading 600 large files through a browser-based tool on a slow or absent connection is not happening.

What I wanted was something closer to a fast, keyboard-driven file browser that understood photos.

Building Foco

So I built Foco.

The core loop is minimal: open a folder, navigate with arrow keys, delete what you don’t want, export what you’re keeping. Deleted files get moved to a _deleted/ subfolder rather than permanently removed, so nothing is ever actually gone until you decide it should be.

Beyond culling, Foco includes a crop and resize editor with live preview, an EXIF panel showing camera body, lens, and exposure settings, GPS coordinates plotted on an embedded map, and a live RGB histogram for a quick read of exposure without opening anything else.

The filmstrip along the side gives you a scrollable overview of everything in the folder at once. It’s virtualised with @tanstack/react-virtual, which keeps scrolling smooth regardless of how many files are in the folder.

Why Electron

This is the question I expected to get asked, and the honest answer is: it was the right tool for the job, even if it’s not the fashionable choice.

I spend most of my time writing React and TypeScript. Using Electron meant I could build a capable desktop app without learning a new language or UI framework from scratch. The alternative would either be learning Swift and SwiftUI for a macOS app, and or creating a native Windows app (I use both MacOS and Windows). I don’t want to mantain two codebases for one app. Electron was the right tool for me.

Electron gets a bad reputation, mostly for memory usage, and sometimes deservedly so. I have felt the pain of large, laggy, RAM hogging Electron apps. But most of those complaints are about poorly optimised apps, not Electron itself. The real constraint for a photo app is not RAM, it’s how you handle image processing. Doing that in JavaScript would be a disaster.

That’s where Sharp comes in. Sharp is a Node.js library built on top of libvips, one of the fastest image processing libraries available. All thumbnail generation, cropping, resizing, and format conversion runs through Sharp via Electron’s IPC layer. The JavaScript side of the app orchestrates the work. The heavy lifting happens in a native module.

Thumbnails are cached to disk with modification time validation, so re-opening a large folder is instant after the first visit. Generation is queued with a concurrency limit of four running Sharp operations, which keeps things moving without overwhelming the system.

The technical parts I found interesting

A few things turned out to be harder than I expected.

Pan and zoom — I wanted the image viewer to feel native. Cursor-anchored zoom (where the image scales from the point under your cursor, not the centre of the viewport) sounds straightforward but requires tracking the mouse position relative to the current transform at every scroll event. I implemented this with ref-based transforms rather than React state. Updating state on every wheel event would trigger a re-render on every frame, which makes scrolling feel sluggish. Refs let you apply CSS transforms directly, keeping it smooth.

The crop overlay — The crop editor involves three coordinate spaces: image pixels, display space (the image scaled and positioned within the viewport), and screen space (actual pixel positions on the monitor). Converting between these when the user is dragging a crop handle, while also accounting for zoom level and pan offset, took several iterations to get right.

Thumbnail caching — Generating thumbnails for a folder of 600 photos on first open takes a moment. But re-opening that folder should be instant. The cache stores thumbnails keyed by file path and modification time. If the file hasn’t changed since the thumbnail was generated, the cached version is used. If it has, it’s regenerated.

Why desktop in 2025

There’s a reflex in web development to assume everything should be a web app. And for most things, that’s correct. But photo triage is genuinely better as a local-first, offline-capable, keyboard-driven desktop experience.

The files are already on my machine. Processing them locally is faster than uploading and downloading them. The app can access the filesystem directly without browser sandboxing getting in the way. Keyboard shortcuts work without fighting browser defaults. And it runs whether or not I have an internet connection.

None of that requires a native app in the strictest sense, Electron is still a browser under the hood, but it does require something that’s not constrained by the web platform.

Where it’s at now

Foco is still in development. The core workflow is solid: open a folder, cull, crop, resize, export. There’s more I want to add, and the repository has the current state of the project.

If you’re in a similar situation, you build primarily for the web but have a problem that’s a better fit for a desktop app, Electron is worth taking seriously. The constraints are real but so is the ability to ship something fast using tools you already know.

Back to blog