I'm rebuilding Flickr!

Well, maybe not, but here’s a description of my recently created photo-sharing webapp. And I have many more ideas to improve this.

Photo Sharing WebApp - Feature Overview

A modern, full-stack travel photo gallery built with Next.js 15, featuring intelligent photo management, interactive maps, and seamless cloud storage integration.


๐ŸŒŸ Highlights

  • Zero-Database Architecture: Uses Vercel Blob for photos and Redis (via Vercel KV) for metadata
  • Privacy Controls: Public, unlisted, and private album visibility options
  • Interactive World Map: Displays photo locations extracted from EXIF GPS data
  • Responsive Design: Optimized for all devices from mobile to desktop
  • Admin Panel: Complete photo management without leaving the browser

Album Management

  • Collapsible Albums: Each travel album can be expanded or collapsed independently
  • Smart Defaults: Most recent album automatically expands on page load
  • Album Metadata: Title, description, and date for each collection
  • Privacy Levels:
    • Public: Visible to everyone on the homepage
    • Unlisted: Only accessible via direct link
    • Private: Visible only to authenticated admins

Photo Display

  • Grid Layout Options: Three display modes to suit your preference
    • Comfortable: Spacious 2-5 column grid with square thumbnails
    • Compact: Dense 3-6 column grid for maximum photos per screen
    • Masonry: Pinterest-style layout preserving original aspect ratios
  • Layout Persistence: Grid preference saved in browser localStorage
  • Newest First: Photos automatically sorted by upload date (newest at top-left)
  • Rounded Thumbnails: Modern, elegant aesthetic with subtle shadows
  • Hover Effects: Smooth scale and brightness animations on interaction
  • Upload Date Display: Shows when each photo was added (in comfortable/masonry modes)
  • Full-Screen Experience: Distraction-free photo viewing
  • Navigation Controls:
    • Keyboard arrows (โ† โ†’) for previous/next
    • On-screen navigation buttons
    • ESC key to close
  • Photo Captions: Optional descriptions displayed below photos
  • Smooth Transitions: Animated photo changes with loading states
  • Mobile Optimized: Touch-friendly controls and responsive sizing

Interactive Features

  • World Map Integration:
    • Leaflet-based interactive map
    • Clustered markers for photos with GPS coordinates
    • Click markers to view photos from that location
    • Automatic bounds fitting to show all locations
    • Album information in marker popups
  • Random Featured Photo:
    • Displays a random photo from all albums on homepage
    • Changes on each page load
    • Shows caption if available
  • Mini Thumbnails:
    • Collapsed albums show preview of first 5 photos
    • Smooth animation on expand/collapse
    • Photo count badge for albums with 6+ photos

Content Syndication

  • RSS Feed: Subscribe to new photo uploads at /feed.xml
  • Analytics: Vercel Analytics integration for visitor tracking

๐Ÿ” Admin Panel Features

Authentication

  • Vercel Authentication: Secure login using your Vercel account
  • Protected Routes: /admin and sub-routes require authentication
  • Session Management: Logout functionality with redirect

Album Management

  • Create Albums:
    • Title (required)
    • Description (optional)
    • Date (optional, ISO format)
    • Privacy setting (public/unlisted/private)
  • Edit Albums:
    • Update all metadata including privacy settings
    • Inline editing interface
    • Cancel without saving
  • Delete Albums:
    • Confirmation dialog before deletion
    • Automatically deletes all photos in album
    • Removes both metadata and blob files

Photo Management

  • Bulk Upload:
    • Multi-file selection support
    • Drag-and-drop interface
    • Progress tracking for each file
    • Batch processing with individual status indicators
    • Total size calculation before upload
  • Upload Features:
    • Automatic EXIF extraction (GPS, camera, capture settings)
    • Optional captions (for single photo uploads)
    • Format support: JPG, PNG, WebP, and more
    • Visual upload progress bar
    • Success/error status for each file
  • Photo Organization:
    • Clickable photo count badges link to management page
    • Grid view of all photos in an album
    • Displays metadata: upload date, GPS, camera info
    • Empty state with call-to-action
  • Delete Photos:
    • Individual photo deletion from management page
    • Confirmation dialog with photo caption
    • Removes both Vercel Blob file and metadata
    • Instant UI update on successful deletion

Admin Dashboard

  • Album Overview:
    • List of all albums (sorted by date, newest first)
    • Photo count for each album
    • Privacy status indicators with icons
    • Quick access buttons (Edit, Upload, Manage, Delete)
  • Status Indicators:
    • ๐ŸŒ Public: Green badge with globe icon
    • ๐Ÿ”— Unlisted: Yellow badge with link icon
    • ๐Ÿ”’ Private: Red badge with lock icon
  • Responsive Layout: Optimized for tablet and mobile management

๐Ÿ› ๏ธ Technical Architecture

Frontend

  • Framework: Next.js 15 with App Router
  • Language: TypeScript for type safety
  • Styling: Tailwind CSS with custom configurations
  • Icons: Lucide React for consistent iconography
  • Image Optimization: Next.js Image component with automatic optimization
  • State Management: React hooks (useState, useEffect)
  • Client-Side Rendering: Dynamic imports for map components (SSR bypass)

Backend

  • API Routes: Next.js server-side API endpoints
  • File Upload: FormData with multipart handling
  • EXIF Parsing: exif-parser library for metadata extraction
  • Image Processing: Automatic format conversion and compression

Storage & Database

  • Photo Storage: Vercel Blob (CDN-backed object storage)
  • Metadata Storage: Redis via Vercel KV Marketplace integration
  • Database Client: ioredis for Redis connections
  • Data Structure:
    • Albums stored as JSON array in albums key
    • Photos stored per album in photos:{albumId} keys
    • No SQL database required

Deployment

  • Platform: Vercel
  • Environment:
    • BLOB_READ_WRITE_TOKEN: Vercel Blob access
    • KV_REDIS_URL: Redis connection string
  • CDN: Automatic edge caching for photos and pages
  • Domain: Custom domain support with automatic SSL

๐Ÿ“Š Data Models

Album

{
  id: string;                          // album_timestamp_random
  title: string;                       // "Japan 2025"
  description: string;                 // Trip description
  date: string;                        // ISO date "2025-01-15"
  createdAt: number;                   // Unix timestamp
  visibility: "public" | "unlisted" | "private";
}

Photo

{
  id: string;                          // photo_timestamp_random
  albumId: string;                     // Reference to album
  blobUrl: string;                     // Vercel Blob URL
  caption?: string;                    // Optional description
  uploadedAt: number;                  // Unix timestamp
  metadata?: {
    gps?: {
      latitude: number;
      longitude: number;
      altitude?: number;
    };
    camera?: {
      make?: string;                   // "Canon"
      model?: string;                  // "EOS R5"
      lens?: string;
    };
    capture?: {
      dateTaken?: string;              // When photo was taken
      exposureTime?: string;           // "1/1000"
      fNumber?: number;                // 2.8
      iso?: number;                    // 100
      focalLength?: number;            // 50mm
    };
    dimensions?: {
      width: number;
      height: number;
    };
    orientation?: number;
  };
}

๐ŸŽฏ User Flows

Visitor Journey

  1. Land on homepage โ†’ See random featured photo
  2. View interactive world map with photo locations
  3. Browse albums (newest expanded by default)
  4. Click photo โ†’ Open lightbox viewer
  5. Navigate with arrows or keyboard
  6. Subscribe to RSS feed for updates

Admin Journey

  1. Navigate to /admin โ†’ Vercel auth redirect
  2. View dashboard with all albums and photo counts
  3. Create new album: Click “Create Album” โ†’ Fill form โ†’ Set privacy
  4. Upload photos: Click “Upload Photos” โ†’ Drag files โ†’ Add captions โ†’ Upload
  5. Manage photos: Click photo count badge โ†’ View grid โ†’ Delete unwanted photos
  6. Edit album: Click “Edit” โ†’ Update metadata/privacy โ†’ Save
  7. Delete album: Click “Delete” โ†’ Confirm โ†’ Album and all photos removed

๐Ÿš€ Performance Optimizations

  • Image Optimization: Next.js automatic WebP conversion and lazy loading
  • CDN Caching: Vercel Edge Network caches photos globally
  • Code Splitting: Dynamic imports for heavy components (map)
  • Responsive Images: Multiple sizes served based on viewport
  • Minimal JavaScript: Client-side JS only where needed
  • Fast Page Loads: Static generation where possible
  • Efficient Queries: Single Redis calls for album/photo lists
  • LocalStorage: Grid layout preference cached client-side

๐ŸŽจ Design Philosophy

Visual Design

  • Color Palette: Blue gradient accents with dark mode support
  • Typography: Clear hierarchy with responsive font sizes
  • Spacing: Generous whitespace for comfortable reading
  • Shadows: Layered depth with hover elevations
  • Animations: Subtle 300ms transitions throughout
  • Icons: Consistent Lucide icon library

User Experience

  • Progressive Disclosure: Collapsed albums reduce cognitive load
  • Keyboard Navigation: Full keyboard support in lightbox
  • Touch Optimization: Tap targets sized for mobile
  • Loading States: Spinners and skeletons during data fetches
  • Error Handling: User-friendly error messages
  • Confirmation Dialogs: Prevent accidental deletions

Accessibility

  • Semantic HTML: Proper heading hierarchy
  • Focus States: Visible keyboard focus indicators
  • Alt Text: Image descriptions for screen readers
  • Color Contrast: WCAG AA compliance
  • Responsive Design: Works on all screen sizes

๐Ÿ“ˆ Use Cases

Personal Travel Blog

  • Document trips with organized photo albums
  • Share adventures with family and friends
  • Keep private memories secure with privacy controls

Photography Portfolio

  • Showcase work by location or project
  • Professional presentation with grid layouts
  • Metadata display for technical details

Family Photo Sharing

  • Create unlisted albums for family-only access
  • Easy upload from mobile devices
  • No complicated software required

Educational Projects

  • Demonstrate modern web development practices
  • Showcase Next.js 15 App Router patterns
  • Example of cloud storage integration

๐Ÿ”ฎ Future Enhancement Ideas

Photo Features

  • Bulk photo deletion (select multiple)
  • Photo reordering (drag-and-drop)
  • Photo editing (crop, rotate, filters)
  • Download full-resolution images
  • Print/photo book export

Search & Discovery

  • Full-text search across captions
  • Tag system for categorization
  • Timeline view (chronological)
  • Favorites/highlights collection

Social Features

  • Photo comments system
  • Social media share buttons
  • Individual photo permalinks
  • Public photo embeds

Analytics

  • View count tracking
  • Popular photos dashboard
  • Storage usage metrics
  • Visitor analytics

Mobile App

  • Native iOS/Android apps
  • Offline photo viewing (PWA)
  • Camera integration for uploads
  • Push notifications for new albums

๐Ÿ’ก Why This Architecture?

Serverless-First

  • No server maintenance: Vercel handles infrastructure
  • Auto-scaling: Handles traffic spikes automatically
  • Global CDN: Photos served from edge locations
  • Cost-effective: Pay only for usage

Modern Stack

  • Type Safety: TypeScript catches bugs at compile time
  • React 19: Latest React features and optimizations
  • Next.js 15: App Router for improved performance
  • Tailwind CSS: Utility-first styling for rapid development

Cloud Storage

  • Vercel Blob: Purpose-built for media storage
  • Redis: Fast key-value storage for metadata
  • No database: Simpler architecture, fewer failure points
  • Atomic operations: Redis ensures data consistency

๐Ÿ“ Technical Decisions

Why ioredis instead of @vercel/kv?

The Vercel KV Marketplace integration provides a standard Redis URL, which works better with the ioredis package. This offers more flexibility and standard Redis features.

Why client-side rendering for the homepage?

The random featured photo and map interactions require JavaScript. Client-side rendering provides the most interactive experience while keeping the codebase simple.

Why separate manage page instead of edit-in-place?

Separating photo management from display keeps each view focused and performant. The upload page stays lightweight for quick uploads, while the manage page provides detailed controls.

Why not use a traditional database?

For a photo gallery, the access patterns are simple (list albums, list photos). Redis provides sub-millisecond reads and sufficient storage for metadata, while Vercel Blob handles the large files. This eliminates the need for PostgreSQL/MySQL.


๐Ÿ—๏ธ Project Structure

voyages-photo-gallery/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ admin/
โ”‚   โ”‚   โ”œโ”€โ”€ manage/[albumId]/       # Photo management page
โ”‚   โ”‚   โ”œโ”€โ”€ upload/[albumId]/       # Photo upload page
โ”‚   โ”‚   โ””โ”€โ”€ page.tsx                # Admin dashboard
โ”‚   โ”œโ”€โ”€ api/
โ”‚   โ”‚   โ”œโ”€โ”€ admin/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ albums/
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ [albumId]/      # Update/delete album
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ route.ts        # Create album
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ photos/
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ route.ts        # Delete photo
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ upload/
โ”‚   โ”‚   โ”‚       โ””โ”€โ”€ route.ts        # Upload photos
โ”‚   โ”‚   โ”œโ”€โ”€ albums/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ route.ts            # Get all albums (public)
โ”‚   โ”‚   โ”œโ”€โ”€ photos/
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ route.ts            # Get all photos (public)
โ”‚   โ”‚   โ””โ”€โ”€ auth/                   # Login/logout
โ”‚   โ”œโ”€โ”€ feed.xml/
โ”‚   โ”‚   โ””โ”€โ”€ route.ts                # RSS feed generation
โ”‚   โ”œโ”€โ”€ layout.tsx                  # Root layout with Analytics
โ”‚   โ”œโ”€โ”€ page.tsx                    # Public gallery homepage
โ”‚   โ””โ”€โ”€ globals.css                 # Global styles
โ”œโ”€โ”€ components/
โ”‚   โ”œโ”€โ”€ AlbumSection.tsx            # Collapsible album component
โ”‚   โ”œโ”€โ”€ GridLayoutToggle.tsx        # Layout switcher
โ”‚   โ”œโ”€โ”€ Lightbox.tsx                # Photo viewer
โ”‚   โ””โ”€โ”€ WorldMap.tsx                # Interactive map
โ”œโ”€โ”€ lib/
โ”‚   โ””โ”€โ”€ db.ts                       # Redis database functions
โ”œโ”€โ”€ types/
โ”‚   โ””โ”€โ”€ index.ts                    # TypeScript type definitions
โ”œโ”€โ”€ middleware.ts                   # Auth protection
โ””โ”€โ”€ package.json                    # Dependencies

๐ŸŽ“ Learning Resources

This project demonstrates:

  • Next.js 15 App Router patterns
  • TypeScript in React applications
  • Tailwind CSS utility-first styling
  • Vercel deployment and storage
  • Redis data modeling
  • EXIF metadata extraction
  • Responsive design principles
  • File upload handling
  • Authentication middleware
  • Dynamic routing
  • Client/server component separation

Perfect for developers learning modern full-stack web development!


๐Ÿ“„ License

MIT License - Feel free to use this as a learning resource or starting point for your own projects.


๐Ÿค Contributing

Built with Claude Code - AI-assisted development for rapid prototyping and feature implementation.

Technologies: Next.js 15 โ€ข React 19 โ€ข TypeScript โ€ข Tailwind CSS โ€ข Vercel โ€ข Redis โ€ข Leaflet

Built by: AI enthusiasts exploring the intersection of modern web development and AI-assisted coding

Building A Dedicated Photo-Sharing Website in Claude Code

Thinking about the upcoming trip to Egypt, I realized I still didn’t have a good solution for sharing photos and comments beyond the usual social networks. Drawing on my experience from the past few weeks deploying web applications on Vercel, I decided to try the same by building a website for sharing and viewing photos. The additional complexity here is that the viewing portion is separate from the photo upload section. Therefore, I need to protect this feature with a password. Additionally, image storage must be optimized to minimize costs and provide a pleasant, flexible viewing experience. I’m using Vercel-only blog storage and Redis for metadata store.

In less than 2 hours, I built a fully functional application with Claude Code and Vercel. Impressive.

Apple Creator Studio - A Few Comments

On today’s announcement by Apple:

  1. Now we know why it took so long for Apple to update Pages, Numbers and Keynote. Where is iWork? Why no iWork subscription without the pro stuff?
  2. I don’t understand this bundling of pro apps with consumer-generalistic apps. Is Apple trying to upsell Pro Apps to consumers via a new subscription? They might be. I don’t think pros want to get Numbers or Pages, though.
  3. Icons are utterly un-Apple, or Apple has become something I no longer relate to software-design-wise. It’s not a good sign.
  4. Yes, Apple does look like Adobe and … Microsoft. And less, Apple.
  5. No mention of Photomator. I believe merging Photomator into Photos is not a good idea; combining a photo browsing app with a photo editing app isn’t ideal. Currently, Photos functions more as a photo browser than as a dedicated editing tool, and both aspects risk becoming mediocre. I want a focused, serious photo-editing app that doesn’t try to rival Photoshop. Is that too much to ask? The challenge lies in Apple’s prioritization of profit over user needs, which makes it difficult to develop such an app.

What a strange start to the year.

On Scrollbars

After reading a recent Gruber article about the macOS Tahoe window-resizing issue, I found a setting in Appearance that keeps scrollbars always visible. It’s somewhat odd because of the scrollbar’s thickness. I wish Apple would make them thinner and less noticeable. I’m unsure if I’ll get used to this.

Note: On Windows 11, scrollbars are always visible by default but are less obtrusive. ๐Ÿคท๐Ÿปโ€โ™‚๏ธ Apple was once known for leading with excellent design and great visual taste, but this is less obvious nowadays.

Auto-generated description: A computer interface displays settings for appearance customization, including options for Liquid Glass, theme colors, and icon styles.

On Appleโ€™s Deal with Google

Back in November, Google announced Private AI Compute, positioning themselves to offer something like Appleโ€™s Private Cloud Compute. It might be something that OpenAI wasnโ€™t willing to do or didnโ€™t see a fit in their business mission. By offering Private AI Compute, Google might have secured the business with Apple. Anyway, itโ€™s becoming impressive how Google is taking back the lead in AI. Lastly, maybe we will see Google Gemini being added to this weekโ€™s next beta of iOS 26.3 to go side-by-side with ChatGPT?

When Things Go Wrong With AI-Generated Code

My first bad experience: the code generated by Claude Code made my dashboard unresponsive in my browser. Eventually, the data stopped updating. After a ten-minute debugging session, I asked Claude Code to revert the change, and it did so promptly. But then I started getting execution failure notices on Discord. A lot of notifications. Then I started investigating…

It appears the browser was making frequent refresh requests to one of my workflows, which depleted my Claude pay-per-use credits. Bummber. Looking at my n8n dashboard, I saw that one of my workflows was failing because of that. Logs were confirming the problem with the interaction with Claude AI. As shown on the graphs below, my instance CPU usage went through the roof. Ouch. Now I know what happened, and the problem was fixed. Now, I should find a way to rate-limit this type of behaviour. That’s for tomorrow, I guess. ๐Ÿ˜…

My Defaults as of 2026-01-10

Changes from the last edition are in bold. โœ‰๏ธ Mail Client: Fastmail ๐Ÿ“จ Mail Server: Fastmail ๐Ÿ“ Notes: Craft + Apple Notes โœ… To-Do: Things 3 ๐Ÿ“ท iPhone Photo Shooting: Camera.app ๐Ÿ“š Photo Management: Photos.app + Photomator ๐Ÿ—“๏ธ Calendar: Calendar.app ๐Ÿ—„๏ธ Cloud file storage: iCloud ๐Ÿ“ฐ RSS: Reeder connected to Inoreader ๐Ÿ“‡ Contacts: Contacts ๐Ÿ•ธ๏ธ Browser: Mobile Safari + ARC Browser on Mac + ChatGPT Atlas ๐Ÿง  AI: ChatGPT + Claude AI ๐Ÿ”Ž Search: Kagi Search ๐Ÿ’ฌ Chat: iMessage (WhatsApp when abroad) ๐Ÿ”– Bookmarks: AnyBox ๐Ÿ‘“ Read It Later: Inoreader ๐Ÿ“œ Word Processing: Ulysses, Craft ๐Ÿ“Š Spreadsheets: Numbers ๐Ÿ› Presentations: Keynote ๐Ÿ›’ Shopping Lists: Reminders ๐Ÿง‘โ€๐Ÿณ Meal Planning: None ๐Ÿ’ฐ Budgeting & Personal Finance: Numbers ๐Ÿ—ž๏ธ News: La Presse (Apple News for English news) ๐ŸŽถ Music: Apple Music ๐ŸŽง Podcasts: Apple Podcasts ๐Ÿ” Password Management: iCloud Keychain & Apple Passwords ๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป Blog hosting: Ghost, Micro.blog, Scribbles.page ๐ŸŒ Web Services: Cloudflare, Chillidog Hosting, DigitalOcean

On Tahoe Icons

Just finished reading “Itโ€™s hard to justify Tahoe icons”, which many UI design pundits and non-UI experts, as well as simple, passionate Mac users, have been referring to a lot recently. I appreciate the documentation effort. It’s really well done. Very convincing. But…

Designers age and are gradually replaced by a younger generation. Whether you like it or not, they bring new beliefs (justified or not), design principles (better or not), and values (questionable or not). Recently, there’s a trend where software appears much less crafted than it once was. Everything seems thrown together, flat. And still…

I like those macOS Tahoe menus with icons (๐Ÿซฃ), but yes, there is an absolute lack of consistency.

An Important Lesson

When I started my studies in computer science over 40 years ago, we learned to read functional specifications and then translate them into machine instructions (COBOL, FORTRAN, Pascal, etc.). It was the training of a programmer. I knew that one day I could become the person who writes functional specifications. I didn’t become a programmer, nor did I work in the development world.

Due to my recent experience with Claude AI, Claude Code, and Vercel to create custom applications, I realize that I have become the one who writes functional specifications, but for processing by artificial intelligence. What does this tell me about the profession of a software developer? The need to write specifications remains essential, if not more so, even with powerful tools like AI. I think it’s a valuable lesson.

On OpenRouter.ai

I just finished reading about the service openrouter.ai. I was curious to understand the purpose of this service as well as its business model. I saw several instances of this service being used in n8n workflows. The problem I see with this service is that it makes the consistency of the quality of responses from the requested LLMs even more unpredictable. Each request could be handled by LLMs with different characteristics and performance from one time to another. I’ll pass on this, but I still learned something tonight.