Reading time badge with vanilla JS

November 29, 2025

Tiny Reading Time Badge (Plain JavaScript)

A small “3 min read” badge under the title can make longer posts feel more approachable. It sets expectations before someone dives in and helps them decide whether to keep reading now or save it for later. This snippet gives you that little detail without bringing in any frameworks, plugins, or build steps.

Everything runs on the front end using plain JavaScript. You decide which container counts as the article and where the badge should appear. Once wired up, the script runs automatically on every page that uses that layout, quietly updating the badge based on the actual text on the page.

What this snippet does

Sometimes you just want a tiny “3 min read” badge under your title without pulling in a whole framework. This snippet:

  • Counts the words inside your main article container.
  • Calculates an estimated reading time (you can tweak the speed).
  • Prints a badge like “3 min read” next to your title.
  • Uses plain JavaScript, no build step, no dependencies.

You give your main content a class (for example .vibe-article) and place a badge span near the title with class .vibe-reading-time. The script does the rest.

Copy-paste snippet (CSS + JavaScript)

The block below contains both the styling for the badge and the script that calculates reading time:

<style>
  /* Tiny reading time badge */
  .vibe-reading-time {
    display: inline-block;
    margin-left: 6px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 12px;
    line-height: 1.4;
    background: #f2f4ff;
    color: #333;
    border: 1px solid #d3d7ff;
    white-space: nowrap;
  }
</style>

<script>
  (function () {
    // Words per minute you want to assume
    var WORDS_PER_MINUTE = 220;

    // Where your main article content lives
    var article = document.querySelector('.vibe-article');
    // Where the badge text should be rendered
    var badge = document.querySelector('.vibe-reading-time');

    if (!article || !badge) {
      return; // nothing to do
    }

    var text = article.innerText || article.textContent || '';
    text = text.trim();
    if (!text) {
      return;
    }

    // Count words
    var words = text.split(/\s+/).filter(Boolean).length;
    var minutes = Math.round(words / WORDS_PER_MINUTE);

    if (!minutes || minutes < 1) {
      minutes = 1;
    }

    badge.textContent = minutes + ' min read';
  })();
</script>

How to wire it up

  1. Wrap your main article content in a container, for example:
    <article class="vibe-article">...your post text...</article>
  2. Add the badge span near your title, for example:
    <h1>My post title <span class="vibe-reading-time"></span></h1>
  3. Paste the <style> and <script> block into your layout (near the bottom of the page or in a shared template used by your articles).

On load, the script looks for .vibe-article, grabs its text content, and runs a simple word count. It then calculates minutes based on your chosen WORDS_PER_MINUTE value and writes the final label into .vibe-reading-time. Short posts are rounded up to at least “1 min read” so you never end up with “0 min”.

Tuning it for your audience

You can easily adjust the assumptions for different types of content:

  • For dense technical posts, you might set WORDS_PER_MINUTE = 180; to account for slower, more careful reading.
  • For lighter blog posts, you could bump it up to 240 if your audience tends to skim quickly.
  • If you publish in multiple languages, you can vary the setting per template based on the language or region.

You can also localize the text of the badge itself. Instead of hard-coding ' min read', you could inject a different phrase per locale, or use an attribute on the badge element to store a translation template if you need more control over wording.

Why reading time badges help

Estimated reading time has become a common pattern across blogs and documentation sites because it reduces uncertainty. Visitors decide in seconds whether they can finish something now or should bookmark it for later. A clear badge right under the title answers that question without them scrolling at all.

It also gives your longer guides a small, friendly touch. Seeing “8 min read” feels more concrete than a huge wall of text with no indication of length. For busy readers, that tiny bit of context can mean the difference between bouncing and staying.

Styling the badge to match your site

The badge style is intentionally minimal so it fits under most headings without overpowering the title. You can tweak:

  • Colors: match the background and border to your brand palette.
  • Spacing: adjust margin-left if your titles are very wide or narrow.
  • Shape: keep the pill look or switch to a small rectangle by changing the border radius.

Because everything is handled on the client side and uses just one tiny script, this approach works well on static sites, simple blogs, and “vibe-coded” projects where you want a quick quality-of-life touch without adding a full-blown reading-time plugin. If you ever decide you don’t want it, you can remove the script and the badge spans and your layout goes back to normal.

Comments (0)

No comments yet — be the first.

← Back to all scripts