How to Automatically Compute Blog Post Reading Times in React
The woosal1337/blog repository calculates reading times dynamically in the browser by counting words within the article element and dividing by a standard reading rate of 220 words per minute.
The woosal1337/blog repository demonstrates an efficient approach to automatically compute blog post reading times without requiring static site generation or build-time processing. By leveraging React's useEffect hook and the browser's native DOM API, the implementation calculates estimated read duration immediately after the component mounts. This client-side method ensures reading times always stay synchronized with current content, automatically updating if the article text changes without requiring a site rebuild.
Understanding the 220 WPM Standard
The implementation relies on WORDS_PER_MINUTE, a constant set to 220 based on average adult reading speeds. This value represents the typical reading rate for comprehension-level consumption of blog content. According to readability research, 220 words per minute strikes a practical balance between speed and retention for general audiences, though you may adjust this constant for technical documentation (slower) or leisure reading (faster).
Implementing the Calculation in components/blocks/post-meta.tsx
The core logic resides in components/blocks/post-meta.tsx, where the PostMeta component handles both client-side calculation and pre-computed frontmatter values. The implementation follows a defensive programming pattern that gracefully handles missing DOM elements.
Defining the Reading Speed Constant
At the module level, the code establishes the reading speed benchmark:
// components/blocks/post-meta.tsx
const WORDS_PER_MINUTE = 220;
Querying the DOM for Article Content
Inside the useEffect hook, the component queries the document for the article element, which serves as the content container:
const article = document.querySelector("article");
if (!article) return;
const text = article.textContent ?? "";
This selector assumes your blog post content wraps the main text in a semantic <article> HTML tag.
Calculating Words and Minutes
The word counting algorithm trims whitespace, splits on the regular expression /\s+/ (one or more whitespace characters), and filters out empty strings to ensure accurate counts:
const words = text.trim().split(/\s+/).filter(Boolean).length;
setReadingMinutes(Math.max(1, Math.round(words / WORDS_PER_MINUTE)));
The Math.max(1, ...) guard ensures every post displays at least 1 minute read, preventing zero-minute calculations for very short content.
Supporting Pre-Computed Frontmatter Values
The component supports hybrid rendering by checking for existing readingMinutes in the post metadata. If the frontmatter already supplies a value, the calculation exits early to respect the static assignment:
useEffect(() => {
if (meta.readingMinutes) return; // Use pre-computed value from frontmatter
const article = document.querySelector("article");
if (!article) return;
const text = article.textContent ?? "";
const words = text.trim().split(/\s+/).filter(Boolean).length;
setReadingMinutes(Math.max(1, Math.round(words / WORDS_PER_MINUTE)));
}, [meta.readingMinutes]);
This flexibility allows content creators to override automatic calculations for posts with heavy images or complex formatting that affects reading speed. Additional formatting utilities from lib/blog-utils.ts handle the display of dates and tags alongside the reading time.
Complete Implementation Example
Here is the complete implementation from the woosal1337/blog source code:
// components/blocks/post-meta.tsx
const WORDS_PER_MINUTE = 220;
export function PostMeta({ meta }: PostMetaProps) {
const [readingMinutes, setReadingMinutes] = useState<number | undefined>(
meta.readingMinutes,
);
useEffect(() => {
if (meta.readingMinutes) return; // use pre-computed value if present
const article = document.querySelector("article");
if (!article) return;
const text = article.textContent ?? "";
const words = text.trim().split(/\s+/).filter(Boolean).length;
setReadingMinutes(Math.max(1, Math.round(words / WORDS_PER_MINUTE)));
}, [meta.readingMinutes]);
/* …rendering logic that shows “{readingMinutes} min read” … */
}
Advantages of Client-Side Calculation
Calculating reading times in the browser offers distinct advantages over build-time generation:
- Dynamic accuracy: Updates immediately reflect content changes without site rebuilds.
- Reduced build complexity: No need to process content during static generation.
- Runtime flexibility: Supports content loaded via CMS or dynamic routes where build-time word counts might be stale.
Summary
- The woosal1337/blog repository uses a 220 WPM constant defined in
components/blocks/post-meta.tsxto benchmark reading speed. - Client-side calculation occurs in a
useEffecthook that queries thearticleelement and counts words usingtextContent.split(/\s+/).filter(Boolean). - Minimum display value: The algorithm enforces at least 1 minute using
Math.max(1, ...). - Frontmatter support: Pre-computed
readingMinutesvalues in metadata bypass automatic calculation when present. - Helper utilities: Additional formatting logic resides in
lib/blog-utils.tsfor date and tag display alongside reading time.
Frequently Asked Questions
What is the standard words per minute rate for calculating blog reading times?
The woosal1337/blog repository uses 220 words per minute, which represents the average reading speed for adult readers consuming general content. Technical documentation or dense academic papers often warrant slower rates (150-180 WPM), while fiction may use faster benchmarks (250-300 WPM).
How does the implementation handle articles with pre-calculated reading times?
If the post metadata includes a readingMinutes property, the useEffect hook exits immediately via if (meta.readingMinutes) return, preserving the frontmatter value. This allows content creators to manually specify reading times for posts where automatic word counting might be inaccurate, such as image galleries or video-heavy content.
Why calculate reading time on the client side instead of during the build process?
Client-side calculation ensures the reading time always reflects the current DOM content, even if the article text changes between builds. This approach eliminates the need to rebuild the entire site when editing content and supports dynamically loaded posts where build-time static analysis is impossible.
What happens if the article element is missing from the DOM?
The code includes defensive checks: if (!article) return prevents errors when the selector returns null. In this case, the readingMinutes state remains undefined, and the component simply omits the reading time display rather than crashing or showing incorrect data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →