A Simple Photo Gallery
4 min

A Simple Photo Gallery

I pulled the photo gallery out of this website and turned it into something you can fork: drop photos in a folder, push, and GitHub gives you a photo site for free.

The Photo Gallery became my favorite part of this website. I've spent an embarrassing amount of time fiddling with it (the grid, the viewer, the captions in monospace, the keyboard navigation); more time than I've spent actually posting photos to it, if I'm honest.

I was motivated to build it, because I haven't found an easy and pleasurable solution to display photos on a website (should be simple, no?). I shared with some friends for feedback, and they also liked it.

Problem is that it only existed inside my Ghost theme. If you wanted something like it, you'd need to run a Ghost server, install my theme, and learn my whole tag system. For what is basically a folder of photographs, that's absurd.

So I spent some sessions with Claude Code pulling it out. The result is simple-photo-gallery: the same gallery as a standalone template that deploys to GitHub Pages for free. No server, no database, no subscription. One npm command (or a fork, if you prefer), push your photos, and a minute later you have a photo site. There's a live demo running some of my photos, and the docs live on the site itself.

Photos
A simple-photo-gallery demo — example photos from arthur.earth.

Folders are albums

The part I care most about is how little you have to do. The content model is
just files:

src/content/photos/
├── sicily-terrasini/        ← a folder = an album
│   ├── index.md             ← optional: title, writeup, captions, order
│   └── *.jpg
├── rooftop-fog.jpg          ← a loose file = a single photo
└── rooftop-fog.md           ← optional sidecar

Drop photos in, done. The markdown files are optional — they're there when you want to curate: per-photo captions, a writeup, a specific order, drafts.

If you've read the fix_6bit_exif bit, you know I have feelings about EXIF. So here the camera metadata actually gets used: albums sort themselves by DateTimeOriginal (the order you shot things), and captions are assembled from a template you control[1] so a photo can caption itself as FUJIFILM X100V · 23MM · F/2.8 · 1/125 · ISO 800 with zero typing. Photos without EXIF just show less; nothing breaks. You commit full-resolution files and the build generates all the resized versions. Everything happens at build time, the site that ships is plain HTML.


  1. The default template is (exifTemplate: '{camera} · {focal} · {aperture} · {shutter} · ISO {iso}') ↩︎

Support for plain pages

A photo site always ends up needing one or two pages that aren't photos (an about, a colophon, that kind of thing). Same logic as the rest: drop a markdown file into src/content/pages/ and it becomes a page, already linked in the menu. The About page on the demo is exactly that — one file, no template code.

The site itself can also take two shapes. Mine is the gallery mode: a front page mixing single photos and albums, with each album getting its own page. But if the whole site is just one thing — a trip, a wedding, an exhibition — there's a single mode that turns the entire website into that one album, either as a grid or as a scrolling photo essay. You don't even have to choose: by default it looks at your folder and figures out which one you meant.

Stack

Astro generates the static site, sharp creates all the resized image versions at build time, and GitHub Actions builds and publishes it on every push. No JavaScript framework — the few interactive parts (the viewer, the lightbox, the mobile menu) are small pieces of plain JS.

The repo contains an AGENTS.md — documentation written for AI agents rather than for people. Architecture, the gotchas we hit, how to extend things. The idea is that if you fork the project and point your own agent at it, it already knows its way around.

Get it!

The easiest way is the scaffolder. If you have Node installed, this is the whole thing:

npm create simple-photo-gallery@latest my-photos

It asks a few questions (title, site shape, chrome), strips out my example photos, and sets up a fresh folder — with placeholder images if you want something to look at right away. Then you create a GitHub repository for it, push, and flip one setting (Settings → Pages → Source → "GitHub Actions"). From then on, every push publishes your site.

If you'd rather start from the source, forking the repo works the same way: enable Pages, replace my photos with yours, push.

GitHub - arthursoares/simple-photo-gallery: Fork-and-deploy photo gallery for Astro + GitHub Pages — Modern-TUI design, masonry grid, full-bleed viewer, EXIF-driven captions
Fork-and-deploy photo gallery for Astro + GitHub Pages — Modern-TUI design, masonry grid, full-bleed viewer, EXIF-driven captions - arthursoares/simple-photo-gallery

That's it. MIT-licensed, no tracking, no framework — your photos in a folder.

If you build a gallery with it, send it my way. I'd genuinely love to see what it looks like with someone else's photographs in it.

See ya!

Subscribe to get e-mail updates

No spam, no sharing to third party. Only you and me.

Member discussion