Skip to content

feature:show estimated time for reading a particular blog over the ca…#290

Open
devlopharsh wants to merge 8 commits intokeploy:mainfrom
devlopharsh:feat/show-estimated-blog-reading-time#3578
Open

feature:show estimated time for reading a particular blog over the ca…#290
devlopharsh wants to merge 8 commits intokeploy:mainfrom
devlopharsh:feat/show-estimated-blog-reading-time#3578

Conversation

@devlopharsh
Copy link
Copy Markdown

@devlopharsh devlopharsh commented Jan 22, 2026

this PR fixed #3578

Related Tickets & Documents

Fixes: #3578

Description

issue - When browsing the blog, there’s currently no indication of how long a post might take to read. This makes it harder for readers to decide which article to open, especially when they are short on time.

solution - Introduce a consistent reading-time estimate for blog posts by calculating it from the full post content (with HTML stripped), and display this estimate prominently on each post card to improve content discoverability and user experience.

Changes

  • Implemented a robust reading-time utility that safely strips HTML and entities before counting words, handling empty content gracefully.
  • Computed reading time at approximately 250 words per minute and rendered it as a top-right “X min read” pill on every PostCard.
  • Updated all post list and mapping components to pass the complete post content into PostCard, ensuring accurate estimates.
  • Extended API list queries and shared fragments to include content, and removed redundant content fetching from single-post queries to avoid duplication.

Type of Change

  • Chore (maintenance, refactoring, tooling updates)
  • Bug fix (non-breaking change that fixes an issue)
  • New feature (change that adds functionality)
  • Breaking Change (may require updates in existing code)
  • UI improvement (visual or design changes)
  • Performance improvement (optimization or efficiency enhancements)
  • Documentation update (changes to README, guides, etc.)
  • CI (updates to continuous integration workflows)
  • Revert (undo a previous commit or merge)

Testing

N/A no specific test needed .

Demo

Before :

Screenshot 2026-01-22 225326

After :

Screenshot 2026-01-22 222535

Environment and Dependencies

  • New Dependencies: N/A .
  • Configuration Changes: N/A.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have added corresponding tests
  • I have run the build command to ensure there are no build errors
  • My changes have been tested across relevant browsers/devices
  • For UI changes, I've included visual evidence of my changes

@devlopharsh
Copy link
Copy Markdown
Author

hii @amaan-bhati @Achanandhi-M @gouravkrosx , please view this PR and acknowledge me accordingly , it will hardly take less than a minute please . Thank you !!

@amaan-bhati amaan-bhati self-requested a review January 29, 2026 18:12
Copy link
Copy Markdown
Member

@amaan-bhati amaan-bhati left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @devlopharsh Thanks for raising this pr and trying to add attention to detail in the blog posts. The idea to display the reading time is nice, but i think its placement can be improved. We can add the reading time right next to the name and the date posted, this would keep the information hierarchy entact, information kept at one single palce is a good practice we should try to focus on here. Hence, lets try to add the reading time right next to the author name and the date posted in the blog card.

@devlopharsh
Copy link
Copy Markdown
Author

@amaan-bhati
Sure sir , I totally understand your concern regarding the design hierarchy of the card and will update the change soon . Please acknowledge me if any other detail is nead to be changed , thank you for the acknowledgement !!

@devlopharsh
Copy link
Copy Markdown
Author

@amaan-bhati, I have shifted the time in the line of details as displayed in the Image. Hope that this is the one you are asking for !!
Please review the change once and let me know if any enhacement needed in the PR , Thank you : )

Screenshot 2026-01-30 003846

@dhananjay6561 dhananjay6561 requested a review from Copilot April 6, 2026 12:06
@dhananjay6561
Copy link
Copy Markdown
Member

Hey @devlopharsh 👋 — thanks for putting this PR together, we appreciate the effort!

We've gone ahead and requested a Copilot review on this. Here's some context from the reviewer:

Performance: adds full post content to every list query just for word count. Download entire blog bodies for cards. Calculate server-side or use excerpt.

Once you've had a chance to go through the comments, please address the feedback and resolve the threads — and we'll get this across the line. Feel free to ask if anything's unclear. Happy coding! 💙

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds “estimated reading time” to blog post cards by computing word count from post content (HTML stripped) and displaying the result in list UIs.

Changes:

  • Introduces a calculateReadingTime utility that strips HTML/entities before counting words.
  • Extends multiple WPGraphQL queries/fragments to include content so list views can compute reading time.
  • Updates PostCard and its call sites to accept content and render a “X min read” label.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
utils/calculateReadingTime.ts Adds HTML/entity stripping and TypeScript typing for reading-time calculation.
lib/api.ts Fetches content in multiple list queries/fragments to support reading-time estimates.
components/post-card.tsx Computes and renders reading time on each post card.
components/topBlogs.tsx Passes content into PostCard for top blog sections.
components/TagsStories.tsx Passes content into PostCard for tag listings.
components/postByAuthorMapping.tsx Passes content into PostCard for author listings.
components/NotFoundPage.tsx Passes content into PostCard for “latest posts” sections on 404.
components/more-stories.tsx Passes content into PostCard for more-stories/search listings.
package-lock.json Updates lockfile name metadata.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// Function to calculate estimated time needed to read content
export function calculateReadingTime(content) {
export function calculateReadingTime(content: string) {
if (!content || typeof content !== "string") {
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function parameter is typed as string, but the implementation still defensively checks typeof content !== "string". Either widen the type (e.g., string | null | undefined / unknown) to match actual call sites, or drop the redundant runtime type check so the signature and behavior stay consistent.

Suggested change
if (!content || typeof content !== "string") {
if (!content) {

Copilot uses AI. Check for mistakes.
Comment on lines 140 to 150
while (hasNextPage) {
const data = await fetchAPI(
`
query AllPosts($after: String) {
posts(first: 50, after: $after, where: { orderby: { field: DATE, order: DESC } }) {
edges {
node {
title
excerpt
content
slug
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getAllPosts() uses a while (hasNextPage) loop and reads data.posts.pageInfo, but the GraphQL query does not request pageInfo { hasNextPage endCursor }. As a result, pagination will never advance beyond the first page. Consider either adding pageInfo to the query or removing the loop and making the function explicitly fetch a single page.

Copilot uses AI. Check for mistakes.
Comment on lines 809 to 816
query AllPostsForSearch {
posts(first: 100, where: { orderby: { field: DATE, order: DESC } }) {
edges {
node {
title
excerpt
content
slug
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding content to getAllPostsForSearch means the search page (and client-side cache in MoreStories) will download full HTML content for up to 100 posts, which can significantly increase build-time payload and client memory. Since the UI only needs a reading-time estimate, consider computing and returning a derived readingTimeMinutes (or wordCount) server-side instead of shipping full content to the browser.

Copilot uses AI. Check for mistakes.
Comment on lines 60 to 70
<PostCard
key={node.slug}
title={node.title}
coverImage={node.featuredImage}
date={node.date}
author={node.ppmaAuthorName}
slug={node.slug}
excerpt={getExcerpt(node.excerpt, 20)}
content={node.content}
isCommunity={true}
/>
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

communityPosts passed into TopBlogs comes from getAllPostsForCommunity (see pages/index.tsx), whose GraphQL query currently does not select content. That means node.content will be undefined here and reading time will fall back to the excerpt (less accurate than the PR’s stated goal). If full-content reading time is required, update the community posts query to include content as well.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +88
<div className="flex items-center gap-1 mb-4">
<span className="text-sm text-gray-500">{author ? author : "Anonymous"}</span>
<span className="text-gray-400">•</span>
<span className="text-sm text-gray-500">
<Date dateString={date} />
</span>
{readTimeLabel && (
<>
<span className="text-gray-400">•</span>
<span className="text-sm text-gray-500">{readTimeLabel}</span>
</>
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions rendering the reading time as a top-right “pill”, but the implementation currently renders it inline in the author/date row. Either update the layout to match the described pill (e.g., absolutely positioned badge) or adjust the PR description to reflect the actual UI.

Copilot uses AI. Check for mistakes.
Comment on lines 28 to +32
const basePath = isCommunity ? "/community" : "/technology";
const cleanedExcerpt = (excerpt || "").replace("Table of Contents", "");
const readingTimeMinutes = calculateReadingTime(content || cleanedExcerpt || "");
const readTimeLabel =
readingTimeMinutes > 0 ? `${Math.max(1, readingTimeMinutes)} min read` : null;
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculateReadingTime(...) runs several regex passes over the full post HTML on every render of PostCard. With content now fetched for list pages, this can become costly when rendering many cards (or when state updates trigger re-renders). Consider memoizing the computed value (e.g., based on content/excerpt) or precomputing reading time in the data layer.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature]: Show estimated reading time on blog posts

4 participants