There are numerous articles out there which give you what I consider fairly convoluted procedures for adding word count and reading time statistics to your posts in the Eleventy static site generator (SSG).
So, how’d you like something simpler? Something you can do strictly within a Nunjucks template? (Except for one teensy little Eleventy filter, that is.)
Here you go.
First, here’s a template file derived from my billboard.njk
:
{% set regExpCode = r/<pre class=(.|\n)*?<\/pre>/gm %}
{% set fixedContent = content | replace(regExpCode, "") | striptags %}
{% set wordCount = fixedContent | wordcount %}
{% set readingRate = 225 %}
{% set readingTime = (wordCount/readingRate) | round %}
{% if readingTime < 1 %}{% set readingTime = 1 %}{% endif %}
<p>
{{ wordCount | numCommas }} words • Reading time: {{ readingTime }} minute{% if readingTime > 1 %}s{% endif %}
</p>
Let’s break down what’s happening here, and keep in mind that everything below (but for, again, one tiny exception) involves stuff built into Nunjucks.
- The
set regExpCode
line creates a regular expression for the HTML that Eleventy wraps around code blocks (at least, that’s true if you’re handling syntax highlighting through the most typical methods; but, if you’re doing it differently, adjust the regex accordingly). - The
set fixedContent
line makes afixedContent
variable, which soon will be used in deriving the word count, and usesreplace
with theregExpCode
variable to delete all code-block-related HTML fromfixedContent
. Then, it uses the Nunjucksstriptags
filter to carve the remaining HTML down to just text. (That neatly takes care of any inline images, among other things.) - The
set wordCount
line uses thewordcount
filter to, well, you can guess. - The
set readingRate
line assigns 225 as the number of words per minute we’ll use in calculating the reading time. If you have a preferred number, substitute it here. - The
set readingTime
line divideswordCount
byreadingRate
and thenround
s it to the nearest integer. (But, since really short posts might end up withreadingTime
as 0 based on thatround
ing, theif readingTime < 1
line fixes that.) - Finally, in the paragraph, we:
- Filter
wordCount
throughnumCommas
(that’s the aforementioned exception, about which more shortly). - Provide
readingTime
, followed by either minute or minutes (depending on whether the value ofreadingTime
exceeds 1).
- Filter
The only other thing this requires is the formatting of the wordCount
so that it has the proper commas for English rendering thereof — accomplished above by use of the numCommas
filter, which comes from this snippet within the Eleventy config file:
eleventyConfig.addFilter("numCommas", function(value) {
return value.toLocaleString()
});
By the way, this also can be done in the Hugo SSG, using various features that come with Hugo out of the box:
{{- /*
h/t to Joe Mooring's answer in
https://discourse.gohugo.io/t/count-word-function-customized-to-exclude-code/34380
*/ -}}
{{- $wordCount := replaceRE `(?s)<div class="highlight">.*?</div>` "" .Content | countwords -}}
{{- $readingTime := div (float $wordCount) 225 | math.Ceil -}}
{{- $wordCountFmt := lang.FormatNumberCustom 0 $wordCount -}}
<p>
{{ $wordCountFmt }} words • Reading time: {{ $readingTime }} minute{{- if (gt $readingTime 1) -}}s{{- end -}}
</p>
Latest commit (08cb79e2
) for page file:
2023-04-17 at 6:18:22 PM CDT.
Page history