Have you ever landed on a blog post and seen a small "7 min read" indicator near the title? It's a small detail, but it's a powerful user experience feature. It helps set expectations and encourages visitors to commit to reading your content. While there are plenty of WordPress plugins that can add this feature, they often come with extra overhead, settings, and potential performance impacts.
As a developer, I often prefer a more lightweight, custom solution. I want full control over the logic and where the data is stored, without adding another plugin to my stack if I can avoid it.
Today, I'm going to share a simple yet powerful PHP snippet I created to automatically calculate the reading time for any post and save it to a custom field. I'll walk you through the code step-by-step, explain why each function is necessary, and then show you exactly how to implement it on your own WordPress site.
The Goal: A Lightweight, Automated Solution
My objective was simple:
- When a post is published or updated, automatically calculate the estimated reading time.
- Save this value (in minutes) to a custom field. For this guide, I'll use a field named
read_time
. - Provide a settings page to retroactively update the reading time for all previously published posts.
A Quick Prerequisite: This code is designed to work with a custom field. My preferred tool for this is Advanced Custom Fields (ACF). Before you start, you'll need to have a custom field plugin installed and have created a Number field named read_time
assigned to your posts.
The Code: A Complete Reading Time Calculator
Here is the full PHP snippet I created. I'll break it down function by function below.
<?php
/**
* Calculate reading time from content.
*
* @param string $content Post content.
* @return int Reading time in minutes.
*/
function calculate_reading_time( $content ) {
$word_count = str_word_count( strip_tags( $content ) );
// The average reading speed is around 200 words per minute.
return ceil( $word_count / 200 );
}
/**
* Update reading time for a post when it's saved.
*
* @param int $post_id Post ID.
*/
function update_post_reading_time( $post_id ) {
// I don't want to run this on revisions or autosaves.
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) {
return;
}
$content = get_post_field( 'post_content', $post_id );
$reading_time = calculate_reading_time( $content );
// This function is from Advanced Custom Fields (ACF).
update_field( 'read_time', $reading_time, $post_id );
}
/**
* Update reading time for all existing posts.
*/
function update_all_reading_times() {
$posts = get_posts( array(
'post_type' => 'any',
'numberposts' => -1,
'post_status' => 'publish'
) );
foreach ( $posts as $post ) {
update_post_reading_time( $post->ID );
}
}
/**
* Add the admin menu page under "Settings".
*/
function reading_time_admin_menu() {
add_options_page(
'Reading Time Settings', // Page Title
'Reading Time', // Menu Title
'manage_options', // Capability required
'reading-time', // Menu Slug
'reading_time_admin_page' // Function to display the page
);
}
/**
* Render the admin page for bulk reading time updates.
*/
function reading_time_admin_page() {
// Check if the form was submitted and verify the nonce for security.
if ( isset( $_POST['update_all'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'update_reading_times' ) ) {
update_all_reading_times();
echo '<div class="notice notice-success"><p>Reading times updated for all posts!</p></div>';
}
?>
<div class="wrap">
<h1>Reading Time Settings</h1>
<form method="post">
<?php wp_nonce_field( 'update_reading_times' ); ?>
<p>Click the button below to calculate and update the reading time for all existing published posts. This may take a moment on sites with many posts.</p>
<input type="submit" name="update_all" class="button-primary" value="Update All Reading Times">
</form>
</div>
<?php
}
// Hook to update reading time automatically when a post is saved.
add_action( 'save_post', 'update_post_reading_time' );
// Hook to add the admin menu page.
add_action( 'admin_menu', 'reading_time_admin_menu' );
Deconstructing the Code: How It Works
Let's walk through each piece of the snippet.
So, what does this code do?
- It creates our function,
get_the_reading_time
. - I've set a default reading speed of 200 words per minute (WPM), which is a pretty standard average.
- It cleans up the post content, removing HTML so we only count the actual words.
- It does a little math: divides the word count by the WPM to get the time.
- It formats the result into the "X min read" text we want to see.
calculate_reading_time( $content )
This is the core logic. It takes the post's content as input and does two things:
strip_tags( $content )
: It removes all HTML tags from the content to ensure I am only counting the actual words, not code.str_word_count(...)
: This PHP function counts the words in the resulting plain text.ceil( $word_count / 200 )
: The final step is to divide the word count by 200 (the average reading speed of an adult) and useceil()
to round the result up to the nearest whole number. This gives me the reading time in minutes.
update_post_reading_time( $post_id )
This function is the automatic trigger. It's hooked into save_post
, so it runs every time a post is saved.
- It first checks if the save event is for a revision or an autosave. If it is, I
return
early to avoid running the code unnecessarily. - It then gets the post's content, calls my
calculate_reading_time()
function to get the value, and finally uses ACF'supdate_field()
function to save the integer to theread_time
custom field for that specific post.
update_all_reading_times()
This function is for handling all the content I've already published. It uses get_posts()
to fetch an array of all published posts (of any post type). Then, it loops through each one and calls update_post_reading_time()
, effectively updating the read_time
field for my entire back catalog.
reading_time_admin_menu()
and reading_time_admin_page()
These two functions work together to create the settings page.
reading_time_admin_menu()
uses WordPress'sadd_options_page()
to add a new sub-menu item called "Reading Time" under the main "Settings" menu in the admin dashboard.reading_time_admin_page()
contains the HTML for that page. It's a simple page with a title, a short explanation, and a button. When the button is clicked, it submits a form. The function checks if the form was submitted, verifies the_wpnonce
for security (to prevent cross-site request forgery), and if everything checks out, it calls myupdate_all_reading_times()
function and displays a success message.
How to Add This to Your WordPress Site
Now for the fun part. Here's how to get this up and running.
Step 1: Create the Custom Field
Make sure you have a plugin like Advanced Custom Fields installed. Create a new Number field with the name read_time. Assign it to appear on the post types you want to have a reading time (e.g., Posts).
Step 2: Add the PHP Code
You have two main options for adding this code:
- (Recommended) A Custom Plugin: This is the best practice. You can create a simple plugin file, paste the code in, and activate it. This way, your functionality isn't tied to your theme.
- (Easy) Your Theme's
functions.php
file: For a quick and easy implementation, you can paste this entire code snippet at the end of thefunctions.php
file in your active theme (preferably a child theme, so your changes aren't lost on a theme update).
Step 3: Update Your Existing Posts
Once the code is added, navigate to Settings > Reading Time in your WordPress admin dashboard. You will see the simple page I created. It's a basic page with only one action (for now).
Click the "Update All Reading Times" button. This will loop through all your old posts and calculate the reading time for each one.
Step 4: Display the Reading Time on Your Site
The final step is to show this value to your visitors. You'll need to edit your theme's template files. This is typically single.php for single posts, or a template part that handles post meta.
Find the spot where you want to display the reading time (e.g., near the author name or date) and add the following PHP snippet:
<?php
$read_time = get_field('read_time');
if ($read_time) {
echo '<span>' . $read_time . ' min read</span>';
}
?>
This code gets the value from the read_time
field and, if it exists, prints it out wrapped in a <span>
tag.
Conclusion
And that's it! With one block of code and a few simple steps, you've added a valuable, lightweight feature to your WordPress site. You have full control over the logic, no extra plugin weighing you down, and a seamless, automated workflow for all future posts. It's a perfect example of how a little custom code can provide a clean and efficient solution.