Owning my reading log
Last updated
Since 2001 or so I keep a list of all the books I have read. It started in the back page of an old school notebook, then eventually I moved to Skoob (the Brazilian Goodreads) and finally landed on Goodreads.
The Goodreads experience
At first I just wanted a place where I could try and beat my reading goals. I’m not that interested in reviewing books or follow what others are reading.
I really just want to look back at the end of the year at that amazingly beautiful shelf full of books. The thing though… you can’t really look at Goodreads, it’s horrendous. They somehow manage to produce the worst quality images for book covers, they are probably running them through a shredder. I know I shouldn’t judge a book by its cover, but I can’t even see the covers in the first place!
Finding and picking the actual edition I read and owned was painful. The UI is ugly and clunky, there’s just too much of it. Every time I opened it to add a book I went “ugh” and vomit a little inside my mouth.
To be fair to Goodreads, its social features are really good, it’s just that I don’t want another social network. When I’m looking for something new to read I just ask friends for suggestions and usually the recommendations are spot on. Although I’ll never forgive the bastard that recommend me the Dubliners. The only good thing about that book is that it ends.
Using Goodreads’ API
I went through a couple of iterations until landing on what you see today.
I started by using gatsby-source-goodreads for pulling my books into Gatsby.js. That was actually super simple to implement, and after a bit of GraphQL and CSS I had the whole page ready.
Althought the shelf was looking much healthier now without the clunkiness of Goodreads interface, there was still that shredded-look for the book covers.
When I eventually redesigned this website using Next.js, I decided to do it all from scratch and stop the dependency on Goodreads altogether.
Moving to Next.js and Contentlayer
When I built my blog I went with Contentlayer to transform my markdown (MDX) posts into a format I could use. I thought of using markdown for the book entries too, but YAML felt better suited for the task.
All book entries are simple YAML files with title
, author
, read
or reading
keys. When I start reading a book instead of read
I just use reading
and that automatically puts into the “currently reading” list. When I finish, I do the inverse and update its date. Neat right?
Here’s how a book entry looks like:
title: The Hitchhiker’s Guide to the Galaxy
author: Douglas Adams
read: 2016-02-12
And here’s the whole Contentlayer document setup:
const Book = defineDocumentType(() => ({
name: "Book",
filePathPattern: `books/**/*.yml`,
contentType: "yml",
fields: {
title: { type: "string", required: true },
author: { type: "string", required: true },
read: { type: "date" },
reading: { type: "date" },
},
computedFields: {
img: {
type: "object",
resolve: (doc) => {
const filePath = `/covers/${doc._raw.sourceFileName.replace(
/\.yml$/,
".jpg"
)}`;
const { width, height } = sizeOf(`public${filePath}`);
return { filePath, width, height };
},
},
},
}));
Notice the computedFields
part. It takes the YAML filename and replaces the extension, so I don’t need to also point where the image is located. I’m also using image-size to get the image’s width
and height
to prevent the dreadful Cumulative Layout Shift.
next/image
is used to serve the correctly sized image for each device, using modern formats, and images only load when they are in the viewport area.
What’s next?
Next I plan to add support for re-reads. I do like to re-read my favourite books every year (hello again Frodo) so that’s definitely coming.
Another thing I want to do is add some simple metrics, like number of books read per year. Maybe the ability to filter by author and genre would be nice too.
Ah, that reminds me, I need to add support for genre. Or maybe I just put a “it’s all fantasy and sci-fi in here” notice somewhere.
Check out the final result live →
Note: some books are still being added to GoodReads because Kindle does it automatically.