Adding Reading Time to an Article

Dec 17th, 2022

0

views

If you've ever viewed an article on Medium, you'll notice that the preview gives an estimated "read time". This feature is easily to implement using the reading-time node package and our existing contentlayer configuration. Begin by installing reading-time.

yarn add reading-time

We will add reading time by adding a new computed field to our post model and defining the type for ReadingTime.

Define the ReadingTime Type

We previously kept our type definitions in /content/defintions. Create a new file reading-time.ts and then use contentlayer's defineNestedType to create the new type.

/content/defintions/reading-time.ts
import { defineNestedType } from "contentlayer/source-files";
 
export const ReadingTime = defineNestedType(() => {
	name: "ReadingTime",
	fields: {
		minutes: {
			type: "number",
			required: true
		},
		words: {
			type: "number",
			required: true
		}
	}
})

Add the ReadingTime Computed Field

Next, open up the post definition file at /content/definitions/post.ts and define the new computed field.

/content/defintions/post.ts
import { defineDocumentType } from "contentlayer/source-files";
import GithubSlugger from "github-slugger";
import { Tag } from "./tag";
import { Series } from "./series";
import moment from "moment";
import readingTime from "reading-time";
import { ReadingTime } from "./reading-time";
 
export const Post = defineDocumentType(() => ({
  // .
  // .
  // .
  computedFields: {
    // .
    // .
    // .
    readingTime: {
      type: "nested",
      of: ReadingTime,
      resolve: (doc) => {
        const time = readingTime(doc.body.raw);
        return { minutes: Math.floor(time.minutes), words: time.words };
      },
    },
  },
}));

Now, we contentlayer generates the typesafe JSON, each post will have a new field called "readingTime" -- an object with two nested components; the minutes it takes to read, and the number of words.

You can then display in a component however you like, accessing those members as follows:

<ReadingTime
	minutes={post.readingTime?.minutes}
	words={post.readingTime?.words}
></ReadingTime>