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 regExpCodeline 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 fixedContentline makes afixedContentvariable, which soon will be used in deriving the word count, and usesreplacewith theregExpCodevariable to delete all code-block-related HTML fromfixedContent. Then, it uses the Nunjucksstriptagsfilter to carve the remaining HTML down to just text. (That neatly takes care of any inline images, among other things.) - The
set wordCountline uses thewordcountfilter to, well, you can guess. - The
set readingRateline 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 readingTimeline divideswordCountbyreadingRateand thenrounds it to the nearest integer. (But, since really short posts might end up withreadingTimeas 0 based on thatrounding, theif readingTime < 1line fixes that.) - Finally, in the paragraph, we:
- Filter
wordCountthroughnumCommas(that’s the aforementioned exception, about which more shortly). - Provide
readingTime, followed by either minute or minutes (depending on whether the value ofreadingTimeexceeds 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 (be74421b4) for page file:
2023-10-06 at 10:19:18 AM CDT.
Page history