Skip to content

How to add automatic Time To Read to Contentful and Gatsby


This quick guide shows you how you can add automatic Time To Read amounts to your Contentful articles that are connected to Gatsby.

The TTR (Time To Read) calculation happens on build time, meaning that it has zero impact on your client-side performance!

As TTR generation happens on build time, you have to utilize the gatsby-node.js file for it. Add this to the gatsby-node.js file:

1// Package below is required for calculating blog post length in words
2const { documentToPlainTextString } = require("@contentful/rich-text-plain-text-renderer")
3// Package below is required for counting the words by turning them into an array
4const words = require('lodash/words')
6// This inserts additional "Time To Read" field into article rich text query
7exports.onCreateNode = ({ node, actions }) => {
8 const { createNodeField } = actions
10 const timeToRead = (content) => {
11 let TTR;
12 // Change the WPM amount (240) below to higher number if your readers are faster
13 // or lower if your readers are slower
14 const avgWPM = 240;
15 const wordCount = words(content).length;
16 TTR = Math.round(wordCount / avgWPM);
17 if (TTR === 0) {
18 TTR = 1;
19 }
20 return TTR
21 }
23 // Change the "ContentfulBlogArticle" below to match your Content Type
24 if (node.internal.type === 'ContentfulBlogArticle') {
25 // Match the "bodyContent" below to match the name of your rich text field
26 const data = JSON.parse(node.bodyContent.raw);
27 const allText = documentToPlainTextString(data);
28 createNodeField({ node, name: 'timeToRead', value: timeToRead(allText) })
29 }

In the example above you need to change ContentfulBlogArticle to match the content type of your blog posts and bodyContent to match the name of your article's rich text field. Also if you don't have the@contentful/rich-text-plain-text-renderer and lodash/words packages installed for doing the word counting, do npm i @contentful/rich-text-plain-text-renderer lodash/words to install the necessary packages.

Now you can query the reading time like this (the number is the reading time in minutes):

Screenshot of a GraphQL query showing the time to read amount

And you would probably use it like this:

<p>Time to read: ${data.contentfulBlogArticle.fields.timeToRead} minutes</p>

So what does it do exactly? Basically the code adds a new node to be part of the GraphQL query. That node is "timeToRead", which is calculated this way: first rich-text-plain-text-renderer is used to turn the rich text into plain text. Then lodash/wordsis used to turn the plain text into an array of words. That array of words gets counted and then divided by the average words per minute reading ability of an average human. It is around 240, so if you want to go with the average, just keep it at 240. The end result of this is how many minutes it takes to read the whole rich text. The result gets rounded to nearest full digit, so if it happens to be rounded to a zero, we round it up to 1 instead. Now this minute amount is available to be queried via GraphQL.