Adding automatic citations to my books

I figured out a neat trick this site can do now that I have my personal library in a NextJS site: automatic citations on every Book page.

The data

A you may see in other notes, this site is powered by my Obsidian notebook, with anything I put in a specific /public folder being published to the site. So I've captured info on almost every book I own in Markdown files within /public/books .

Actually...

at time of writing most of them are hidden away in /private/books with a note in their body saying "#todo: write a review". I need to learn to read and write faster!

The data for these books is stored in the YAML frontmatter data at the top of these files. Here's an example from The Wind-Up Bird Chronicle:

---
title: The Wind-Up Bird Chronicle
author: Haruki Murakami
coverImg: wind-up-bird-chronicle.jpg
publisher: Vintage Books
publishLocation: "New York, NY"
publishDate: 1997
firstPublished: 1994
category: Fiction
language: en
translator: Jay Rubin
coverType: Paperback
pages: 607
format: Printed Book
locId: "PL856.U673N4513 1997 895.6'35-dc21"
isbn: 0-679-44669-9
tags: "fantasy, surrealism"
isBorrowed: true
borrowedBy: Dan Stavarz
rating: 5
status: Read
created: 2022-06-22
updated: 2022-06-22
---

A lot of data to work with!

For new books I've been reading I have been putting the citation at the top of its page as a part of my Zettlekasten-esque literature notes process. I had been dreading doing the same thing for the 150+ other books I still have to get published on here, when the thought struck me that I could templatize it with all the data I've got on my books!

The code

Below is the code I used to generate a general-purpose MLA citation. Note that I used a more generic interface for the component by wrapping with React.PropsWithoutRef as opposed to using ICitation directly. This means that I should in theory be able to use the Citation component with things other than my Books collection, as it ignores all other props passed to it.1

The comments you see next to each field in the interface definition were pulled directly from a guide for writing MLA citations from Scribendi.com. It's pretty wonderful how conceptually easy it is to map to TypeScript from help articles like these. I was reading this article and saw a bulleted list of what's included in an MLA citation and thought "oh wait I can use that as is!" I think I'm being won over to the "TypeScript is magic" crowd 🦄.

// ./components/Citation.tsx

interface ICitation {
    author?: string, // Author's name
    title: string,// Full title of each publication (from the title page, not the front cover)
    subtitle?: string,
    publisher?: string, // Publisher
    publishLocation?: string, // City of publication (cite only the first city if there is more than one)
    firstPublished?: number, // Date of publication
    publishDate: number,
    edition?: number, // Volume and issue numbers, if available (for journals)
    format: string, // Medium of publication or reception (print, web, radio, television, LP, CD, etc.)
    
    pageRange?: string, // Page numbers you have referenced
    subworkTitle?: string, // Only for smaller works within an larger one
}

export default function Citation(props: React.PropsWithoutRef<ICitation>) {
    const {
        author,
        title,
        subtitle,
        publisher,
        publishLocation,
        firstPublished,
        publishDate,
        edition,
        pageRange,
        format,
        subworkTitle
    } = props

    const maybeSwappedName = (author && !author?.includes(','))
        ? author?.slice(author?.lastIndexOf(' ')) + ', ' + author.slice(0, author.lastIndexOf(' '))
        : author

    return (<p>
        {maybeSwappedName ? maybeSwappedName+'. ' : 'Author Unknown. '}
        {subworkTitle ? `"${subworkTitle}." ` : ''}
        <em>{title}{subtitle ? ': ' + subtitle : ''}. </em>
        {edition ? ` ${edition}. `: ''}
        {publishLocation ? `${publishLocation}: ` : ''}
        {publisher ? `${publisher}, ` : ''}
        {firstPublished || publishDate}. {pageRange ? pageRange+'. ' : ''}
        {format}.
    </p>)
}

Now within my ./pages/books/[slug].tsx Book template page I can simply drop my book into this citation and have one appear on every entry's page!

import Citation from 'components/Citation'

const BookTemplate: NextPageWithLayout = (props) => {
	// ...
	return (<>
			// ...
		<div className="max-w-4xl mx-auto cl-book-body">
		  <ReactMarkdown>
			{bookBody}
		  </ReactMarkdown>
		  <h2>Citation</h2>
		  <Citation {...book} /> 
		</div>
	  </article>
	</>)
}

Conclusion

That's the entirety of the changes that were made in the commit for this feature! I'm really happy with how much value this adds to my site with just one small commit's worth of work. I also think the serendipity and speed of this feature is already showing the knock-on benefits of having my second brain be my CMS.

What do you think? Reach out to me if you find it interesting, and happy coding 🤙🏻

Footnotes

  1. Interestingly, this idea of an interface that just worries about if its necessary pieces are present reminds me of how traits work in Rust. I don't know type theory though so I might be way off base.

Other content that links to this