npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@binarynoir/vitepress-reading-time-tag

v0.1.10

Published

Write [[readingTime]] in VitePress 2 Markdown and get a "Reading time" tip block with the page's estimated reading time, calculated at build time.

Readme

vitepress-reading-time-tag

npm version CI license

A VitePress 2 plugin that turns [[readingTime]] into a tip block showing how long the page takes to read. The figure is calculated at build time from the page's own Markdown, so there is no component to register, no stylesheet, and nothing extra shipped to the browser.

Documentation and live demo

Support me on Buy Me a Coffee Support me on Ko-fi More tools

What this does

# Deploying the API

[[readingTime]]

Start by ...

renders as VitePress's tip block:

Reading Time Reading time for this document is 3 minutes for 705 words.

Install

npm install @binarynoir/vitepress-reading-time-tag

Usage

Register the plugin in VitePress's own markdown.config hook, the same place you'd add any other markdown-it plugin:

// .vitepress/config.mts
import { defineConfig } from 'vitepress';
import { readingTimeTag } from '@binarynoir/vitepress-reading-time-tag';

export default defineConfig({
  markdown: {
    config(md) {
      md.use(readingTimeTag);
    },
  },
});

Then put [[readingTime]] on its own line in any page. Case doesn't matter, and spaces inside the brackets are fine ([[ readingTime ]]).

Options go as the second argument (md.use(readingTimeTag, { /* ... */ })), and other markdown-it plugins can share the same config(md) function:

config(md) {
  md.use(readingTimeTag);
  md.use(somethingElse);
},

withReadingTimeTag shortcut

If you'd rather wrap your config than touch markdown.config:

import { withReadingTimeTag } from '@binarynoir/vitepress-reading-time-tag/vitepress';

export default withReadingTimeTag(
  defineConfig({
    // ...your normal config
  }),
);

It is equivalent to the markdown.config version above. It will not clobber a markdown.config you already have, including one set by another withX() wrapper. It installs its own first, then calls yours with the same arguments.

Plain markdown-it

import MarkdownIt from 'markdown-it';
import { readingTimeTag } from '@binarynoir/vitepress-reading-time-tag';

const md = new MarkdownIt().use(readingTimeTag, {
  render: ({ minutes }) => `About ${minutes} min read`,
});

The default output uses ::: tip container syntax, which VitePress provides. In a bare markdown-it setup, pass your own render.

How the time is worked out

Words are counted from the page's Markdown, then divided by wordsPerMinute and rounded up, so a page never reads "0 minutes". These are left out of the count:

  • frontmatter and fenced code blocks
  • HTML tags and comments (their text is still counted)
  • link targets and bare URLs (link text is still counted)
  • the [[readingTime]] tag itself

Each CJK character counts as one word. Inline code counts as ordinary words.

Options

Both readingTimeTag(md, options) and withReadingTimeTag(config, options) take:

| Option | Default | Description | | ---------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | wordsPerMinute | 300 | Reading speed. Must be a positive number; anything else throws when the plugin is registered. | | render | a ::: tip container | ({ minutes, words }) => string. Returns the Markdown that replaces each tag, so it can be any Markdown. A container must start at a line start. |

withReadingTimeTag(config, {
  wordsPerMinute: 238,
  render: ({ minutes }) => `::: info\n${minutes} min read\n:::`,
});

Code is left alone

A tag inside a fenced block (``` or ~~~, including unclosed ones) or inline code is never replaced, so you can document the syntax itself:

Write `[[readingTime]]` to add the block.

Indented (four-space) code blocks are not detected; use a fenced block.

API

import { calculateReadingTime, transformReadingTimeTags } from '@binarynoir/vitepress-reading-time-tag';

calculateReadingTime(source: string, wordsPerMinute?: number): { words: number; minutes: number }
transformReadingTimeTags(source: string, options?: ReadingTimeTagOptions): string

calculateReadingTime is useful on its own, for instance to show a reading time in a custom layout. countWords and renderReadingTimeTip are exported too.

Differences from the VuePress version

The original plugin emitted {{ $page.readingTime.minutes }} and relied on VuePress's reading-time plugin to fill it in. VitePress has no $page, so this package does the counting itself. Wording also changed: "3 minutes" and "1 word" instead of "3 minute(s)" and "1 word(s)", and minutes are whole numbers.

Releasing

Releases are tag-triggered. From a clean main that's in sync with origin/main:

npm run release:patch   # or release:minor / release:major

This runs typecheck/lint/test/build locally, then npm version <bump> and git push --follow-tags. Pushing the tag triggers .github/workflows/release.yml, which re-runs the checks, publishes to npm, and creates a GitHub release.

License

MIT


Feedback and support

Bug reports, feature requests and questions are all welcome. Please open them on this project's GitHub issue tracker:

Pull requests are welcome too.

More from BinaryNoir

Reading Time Tag is one of a family of open-source tools from BinaryNoir.

Pairs well with Reading Time Tag

New: Blog: posts anywhere, a main blog and section blogs, with RSS feeds

See them all: VitePress plugins, Vite plugins, editor extensions and command-line tools, with documentation and live demos, at binarynoir.tech.

Author

John Smith III, BinaryNoir. Thanks to all contributors and users for their support and feedback.