Photography Portfolio
A photography portfolio that loads fast despite being packed with high-res images. Built with Eleventy and Node.js, featuring automated image optimization and offline support.

Technology Stack
Core Framework
Performance & Optimization
The Problem
Photography portfolios face a brutal trade-off: we want to show high-quality images, but large files make our site painfully slow, especially on mobile. Most solutions involve manually resizing images or compromising on quality.
I wanted my portfolio to feel instant while still showcasing photos at their best. That meant automating everything—no manual image processing, no quality loss, and fast loading even on slow connections.
How I Solved It
The solution came down to building a smart image pipeline that runs automatically during the build process. Here's what happens:
Automated Image Processing
I built a Node.js script using Sharp.js that processes every image at build time. It generates three sizes (thumbnail, medium, large) in three formats (AVIF, WebP, JPEG) so browsers can pick the best one.
The result? Images that are 60-80% smaller with no visible quality loss. And since it checks file timestamps, it only processes new or changed images, not the entire library every time.
// For each image, generate 3 sizes × 3 formats = 9 variants
const sizes = [
{ width: 400, suffix: 'thumb' },
{ width: 800, suffix: 'medium' },
{ width: 1200, suffix: 'large' }
];
const formats = ['avif', 'webp', 'jpeg'];
// Sharp.js handles the heavy lifting
await sharp(inputPath)
.resize(size.width)
.toFormat(format, { quality: 85 })
.toFile(outputPath);Fast Page Loads
Beyond image optimization, I focused on making the initial page load feel instant:
- Critical CSS is inlined so the page renders immediately without waiting for stylesheets
- Images below the fold use lazy loading—they only load when you scroll to them
- All assets (HTML, CSS, JS) are minified automatically during the build
Offline Support
The site works as a Progressive Web App, meaning once you've visited a page, it's cached and works offline. A custom service worker handles this smartly:
- Images and fonts load from cache first (they rarely change)
- HTML pages check the network first for fresh content
- CSS and JS use a stale-while-revalidate strategy for the best of both worlds
Easy Updates
Adding new photos is simple: create a Markdown file with some front matter, drop in the image, and run the build. Everything else happens automatically: image processing, page generation, cache updates.
I also wrote a few npm scripts to make common tasks easier, like creating new posts or batch processing images.
Results
The site hits 98+ on Lighthouse performance metrics and feels fast even on slow mobile connections. Images look sharp, pages load instantly, and it works offline.
More importantly, maintaining it is painless. I can add new photos in under a minute without thinking about image sizes, formats, or optimization—the build process handles all of that.