Every year I tell myself I’m going to do a proper year-in-review, and every year I don’t. This year I finally built the infrastructure to make it easy – and I thought I’d share how it came together.
The 2025 page pulls together everything I tracked throughout the year: blog posts, movies watched, books read, albums listened to, games played, and links shared. It’s all powered by Astro’s content collections, which made aggregating all this data surprisingly painless. The actual building? That was the easy part. The hard part was realizing how bad I am at logging things consistently.
The Data Model
My site tracks different content types in separate collections – posts, movies, books, music, games, shows, and links. Each collection has its own schema, but they all share a date field. That’s the key to making a year-in-review work: filter everything by year and merge them into a single timeline.
Astro makes this almost too easy:
import { getCollection } from 'astro:content';
const allPosts = await getCollection('posts');
const allMovies = await getCollection('movies');
const allBooks = await getCollection('books');
// ... etc
// Filter to 2025, tag with type, merge, sort
const posts2025 = allPosts
.filter(post => !post.data.draft && post.data.date.getFullYear() === 2025)
.map(post => ({ type: 'post' as const, date: post.data.date, data: post }));
const allTimelineItems = [
...posts2025,
...movies2025,
...books2025,
// ... etc
].sort((a, b) => b.date.getTime() - a.date.getTime());
That’s it. Fetch all collections, filter to the year, map each item to a common format with a type tag, merge into one array, sort by date. I have a chronological list of everything I did in 2025.
Well. Everything I remembered to log.
The Logging Problem
Here’s the embarrassing truth: I read way more books than show up on this page. I watched movies I never added to Letterboxd. I listened to albums I forgot to rate on Record.club.
The stuff that is tracked? It’s tracked because I have scripts that sync from external services. Letterboxd has an RSS feed. Record.club has an API. I wrote Bun scripts that pull data from these services and generate markdown files with the right frontmatter. Set it and forget it.
Books? Those I add manually. Which means I added maybe a quarter of what I actually read. Same with games – if I didn’t think to create the file right after finishing something, it just… didn’t get logged.
Lesson for 2026: Use more services with APIs. Goodreads exists. Backloggd exists. The friction of manual entry is the enemy of good data. If I have to open a text editor and remember the exact frontmatter format, it’s not happening.
The Polaroid Selfies
The most fun part was the selfie section. I take a lot of selfies throughout the year, and I wanted to display them like scattered polaroids on a table.
Astro’s import.meta.glob pulls in all images from a folder, and CSS does the heavy lifting for the scattered effect. Each polaroid gets positioned based on its index using trigonometry to create a spiral pattern – the kind of math I never thought I’d use after college but here we are. The “Shake” button randomizes positions, and “Order” arranges them in a neat grid.
One thing I learned the hard way: check your image sizes! My selfies folder ballooned to 100MB because I forgot to resize some photos. A quick script using macOS’s sips tool fixed that – the folder went from 100MB to 16MB. The largest file dropped from 22MB to 78KB. For images displayed at 200px, 800px width is plenty.
The Timeline
The timeline alternates items left and right, with a central spine connecting them. Posts get full cards with descriptions and tags, while media items get compact badge-style cards with thumbnails. It looks nice! And it was genuinely fun to scroll through and remember “oh right, I did that.”
Which brings me back to the logging problem. The timeline has gaps. Not because nothing happened, but because I didn’t write it down. There are whole months where it looks like I just… stopped consuming media. I didn’t. I just stopped tracking it.
What I’m Doing Differently in 2026
-
Actually use Letterboxd – I have the account. I have the sync script. I just need to rate the movie when I watch it, not three weeks later when I vaguely remember whether I liked it.
-
Pick a book tracking service and stick with it – StoryGraph, or something with an API so I can automate the import.
-
Start earlier – I built this page on January 1st. Starting in December would let me actually enjoy filling in the content instead of rushing to remember what I did in February.
Building this page reminded me why I love the web. It’s just HTML, CSS, and some TypeScript glue – no complex frameworks, no build pipelines that take minutes. Astro gets out of the way and lets you build things that feel personal and fun.
Here’s to better logging in 2026.