Show Estimated Reading Time in WordPress (1 function, no plugin)
Calculate and display estimated reading time in WordPress without a plugin. One helper function counts words, strips shortcodes and HTML, divides by 225 (the average adult reading speed), and renders “7 min read” anywhere you want it.
Medium popularised the reading-time pill in 2013 and readers have expected it since. Most WordPress plugins for this ship 30+ KB of JS to do one tiny calculation. This is a 15-line PHP helper. Drop it in, call it in your template or via the shortcode, done. On gauravtiwari.org it renders under every article longer than 500 words.
What this snippet does
- Counts words in the post content and divides by 225 words per minute
- Strips shortcodes and HTML tags before counting so the number reflects actual readable text
- Rounds up to avoid the “0 minute read” problem on very short posts
- Returns a string like “7 min read” that you can drop anywhere — byline, card, archive
- Ships a
[reading_time]shortcode for use inside post content or patterns - Skips the label on posts under 500 words so short notes stay uncluttered
Install and use
Paste into your child theme functions.php or a site-specific mu-plugin. Call gt_reading_time() inside the loop, or use the [reading_time] shortcode. For Block Themes, add it to a pattern or template part via a Shortcode block.
<?php
/**
* Estimated reading time for WordPress posts.
* Uses 225 words-per-minute (WPM), the average adult reading speed.
*/
function gt_reading_time( $post_id = null, $wpm = 225 ) {
$post_id = $post_id ?: get_the_ID();
if ( ! $post_id ) return '';
$content = get_post_field( 'post_content', $post_id );
$content = strip_shortcodes( $content );
$content = wp_strip_all_tags( $content, true );
$words = str_word_count( $content );
$minutes = (int) max( 1, ceil( $words / $wpm ) );
return sprintf(
/* translators: %d: minutes */
_n( '%d min read', '%d min read', $minutes, 'gt' ),
$minutes
);
}
/**
* Shortcode: [reading_time] or [reading_time wpm="200"]
*/
add_shortcode( 'reading_time', function ( $atts ) {
$atts = shortcode_atts( array( 'wpm' => 225 ), $atts, 'reading_time' );
return esc_html( gt_reading_time( null, (int) $atts['wpm'] ) );
} );
/**
* Example: prepend reading time to every post longer than 500 words.
*/
add_filter( 'the_content', function ( $content ) {
if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
$words = str_word_count( wp_strip_all_tags( $content ) );
if ( $words < 500 ) return $content;
return '<p class="reading-time">' . esc_html( gt_reading_time() ) . '</p>' . $content;
} );How it works
The helper pulls raw post content, strips shortcodes (so [gallery] or [product_box] do not pollute the count), strips HTML, then uses PHP’s str_word_count() for a fast count. Dividing by 225 WPM is the defensible midpoint — Jakob Nielsen pegs average silent reading at 250 WPM for non-fiction, 200 WPM for technical content. ceil() plus max(1, ...) keeps a 50-word post at “1 min read” instead of zero. The shortcode wraps the helper with a customisable WPM. The optional filter block appends a visible label only to posts over 500 words so your definition of a long-read is explicit.
Download and source
- Gist: gist.github.com/wpgaurav
- Bundled in the Functionalities plugin as a toggleable module
- Framework-agnostic — works on any classic or block theme
FAQs
Why 225 words per minute?
It’s the defensible midpoint of published reading-speed research. Nielsen puts average silent reading at 250 WPM for non-fiction, 200 WPM for technical or academic content. 225 is where most reading-time implementations settle. Pass a different WPM via the function parameter or shortcode attribute if your audience is faster or slower.
Does this count images, code blocks, or embeds?
No. wp_strip_all_tags removes everything that isn’t text, so code block contents are counted (the code is text) but image alt attributes are not. If you want images counted as 12 seconds each (Medium style), add $image_count * 12 seconds to the total before dividing.
Will this slow my site down?
No. It runs once per request, does a trivial word count, and adds sub-millisecond overhead. Cache the value in post meta if you serve millions of pageviews and want to shave even that.
Does it work with patterns and synced patterns?
Yes. strip_shortcodes and wp_strip_all_tags flatten block comment syntax before counting, so content inside patterns is counted as normal text.
How do I add it to the Marketers Delight byline?
Hook into md_hook_byline_after_date, which is the same action I use on gauravtiwari.org. The full callback ships in the Functionalities plugin reading-time module.
Can I show “2 min listen” for podcast posts?
Yes — override the output string. For audio, divide by 150 instead of 225 because spoken word is slower than silent reading.