Adding Reading Time to an Article

Dec 17th, 2022

views

likes

292 words (1 minutes)

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 pacakge 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>

Next.js