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 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());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.
What is tracked gets 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.