Where is Kangabru?
My personal travel photo blog used to share my adventures with family and friends. Browse through thousands of hand picked photos, see where I've been, or even play the game!
Contents
Key Points
- A Next.js site showcasing photo albums from my travels. This involved a complete rebuild of my 2019 website to update it with a new look and a ton more features.
- This personal project allowed me to indulge in creating flashy features and exploring new technologies. For instance, I incorporated vibrant animations (particularly in the game) using Framer Motion. I also developed unique features like a custom masonry grid with multi-column support for wider photos.
- I thoroughly enjoyed the challenge of building a media-heavy site that is also performant. For instance, some pages can load 100s of images, so I implemented a feature that dynamically removes images not on screen.
Features
Where is Kangabru?
is all about showcasing beautiful places that I have visited in a fun and interactive way. Friends and family can track my whereabouts and discover my top travel spots across the world.
It offers:
An interactive map that displays my current and past locations
A vast collection of my travel albums, photos, and videos.
Even a playable game inspired by GeoGuessr!
The Tech
The tech stack for this project improves on the original with some significant improvements:
- I switched from using a client-side app to Next.js. This not only improves the developer experience but also handles the little things better like routing, code splitting, social images, and image optimization etc. It just does things better than my custom app did before.
- Firebase is out, and local JSON files are in. After years of using the site, I realized Firebase was an overkill. JSON files are faster to load and simpler to manage.
- I've replaced old libraries for better map, animation, and React state support.
Masonry Grid
- The masonry grid is a way to display images in columns while preserving their original proportions. This creates a visually interesting staggered row effect.
- A standout feature of this grid is its smart rendering. It only loads elements when they come into view on the screen. This ensures a fast and efficient page load, even when an page has 100s of large images.
- I've also added support for multi-column images, which presented a fun challenge. This feature ensures that super wide images are clearly visible and not squeezed into thin columns. This was an interesting challenge to solve as wide images can create gaps above them which are filled after-the-fact.
World Map
- A key aspect of the site is the interactive map that displays albums. I utilized Mapbox for this, specifically their GL version that supports vector tiling. This was a significant upgrade from the old site's raster-based images, which were not only slow to download but also appeared blurry when zoomed in.
- One challenge I faced was integrating the library with my React components - such as in the game, or when a user filters locations which updates the map. There were many small features where I had to carefully manage the internal state to work well with Mapbox.
- Another hurdle was grouping nearby album 'clusters' on the map. While Mapbox does offer some clustering support, it took a lot of trial and error to get it working seamlessly.
Removing Firebase
The old site:
- The previous version of the site was built entirely on Firebase. It hosted the frontend, used 'Firestore DB' for album storage, and 'Firebase Storage' for image hosting.
- I built a simple admin (using Firebase auth) UI that allowed me to create albums, modify data, and upload photos.
- 'Firebase functions' were used to resize photos, which were triggered upon image upload.
While this setup worked well, it required constant upkeep. For instance at one point some 'Firebase functions' APIs were depreciated so the functions stopped running. I also had to maintain the extra admin UI and handle security via auth. Firebase makes that stuff easy, but it was still a time-consuming task.
The new site:
- After a few years, I realized that storing records in Firebase wasn't providing any significant benefits. I already edit photos on my computer, so using local processing scripts and JSON data files was a better solution. Now, I just run a single command that:
- Resizes and compresses photos locally, which is far easier to use/test/debug compared to serverless functions.
- Uploads files to Firebase Storage as before and returns the URL.
- Generates JSON data files that are committed to GIT. This allows me to test data-affecting features without the risk of data loss. I also use zod to verify data and type integrity before committing any data changes.
In essence, I simplified the old process where a more scalable solution wasn't necessary. It was a good reminder of the KISS principle.
Video Support
One key upgrade was support for video content. Now I can upload short mobile videos and longer edited ones as part of my albums.
For hosting longer videos I use Cloudflare Stream (CFS). It's low-cost and offers a viewing experience similar to YouTube, where the client can switch between different qualities for uninterrupted viewing. Videos stream quickly thanks to its global CDN making it easy for my friends and family around the globe to view them. Even videos that are a few GBs in size are viewable instantly through CFS.
Framer Motion
Framer Motion is a key tool I use for creating animations on the site. As it's a personal website, I enjoyed making it more lively and playful than your average business product.
This is particularly noticeable in the game, where elements spring in and out of view during gameplay.
The masonry grid used by albums to display images is another example. Everything animates smoothly whether you're scrolling down to see new images, changing the grid size, or clicking on an image to expand it. These animations give the page a sense of life and help users track elements as they move around the screen.