reading-time
Fast, dependency-free JavaScript library that estimates reading time from plain text, Markdown, or HTML.
Repository Health
Technical Analysis
reading-time is a lightweight, zero-dependency TypeScript library that estimates how long it will take an average person to read a piece of text, mirroring the reading-time estimate popularized by Medium. It counts words directly from a string and converts that count into a minutes/milliseconds estimate using a configurable words-per-minute baseline, working reasonably well across plain text, Markdown source, and raw HTML without needing a separate parser.
Beyond the core readingTime() function, the library exports a ReadingTimeStream Transform stream for computing a word count incrementally over large files without loading them fully into memory, plus lower-level countWords() and readingTimeWithCount() helpers for callers who want to split counting and time calculation. It also includes explicit word-boundary handling for CJK scripts (Hiragana, CJK Unified Ideographs, Hangul), a detail most naive word-counters miss.
What You Get
- Zero-dependency
readingTime(text, options)function returning minutes, milliseconds, and word count - Standalone
countWords()andreadingTimeWithCount()helpers for building custom counting pipelines ReadingTimeStreamNode.js Transform stream for counting words across large files without buffering them in memory- Built-in CJK-aware word boundary detection (Hiragana, CJK Unified Ideographs, Hangul) alongside standard whitespace-based counting
- Full TypeScript type definitions shipped with the package
Common Use Cases
- Blog and CMS platforms showing an “X min read” badge next to each article
- Static site generators computing reading time at build time for Markdown content
- Documentation sites estimating time-to-read for long-form docs pages
- Content pipelines streaming very large text files through
ReadingTimeStreamto avoid loading them fully into memory
Under The Hood
Architecture
The package is organized into three small, single-purpose modules: src/reading-time.ts holds the pure word-counting algorithm and time-conversion math, src/stream.ts wraps that same counting logic in a Node.js Transform stream (ReadingTimeStream) for incremental processing, and src/index.ts unifies both under a single entry point while shimming module.exports so the package works with both require('reading-time') and ESM-style default imports. There is no internal dependency injection or layering beyond this — the whole library is a thin, composable set of pure functions plus one stateful stream class, so the entry point is also effectively the full public API surface.
Tech Stack
Written entirely in TypeScript with zero runtime dependencies, compiled via tsc (see tsconfig-build.json) into a CommonJS dist/ output for Node and a separate browser-facing bundle referenced by the browser field in package.json. The devDependency set is limited to typing and tooling: @types/* packages, typescript, eslint with @typescript-eslint, and mocha/ts-mocha/chai for tests. CI runs on GitHub Actions against multiple Node versions (12 and 14 per the checked-in workflow), executing npm run build followed by npm test.
Code Quality
Tests live under test/ (reading-time.spec.ts, stream.spec.ts) and use mocha with chai assertions, covering both plain-English word counting edge cases (leading/trailing whitespace, links, single-character input, empty strings) and a dedicated suite of CJK scenarios (Japanese, Korean, mixed Latin/CJK text, Katakana handling). ESLint is configured with the @typescript-eslint/recommended ruleset plus a custom shared config, and the public API is fully typed through src/types.ts. Naming is consistent and functions are small and pure, which keeps the test surface manageable despite the lack of a dedicated CONTRIBUTING guide.
API Design
The public API is deliberately minimal: a single default-exported readingTime(text, options) call covers the common case with a sane 200-words-per-minute default, while countWords() and readingTimeWithCount() are exposed separately for callers who need to split counting from time calculation (e.g. to aggregate counts across chunks before computing a final time). An optional wordBound function lets callers override what counts as a word boundary, and the CJK-aware detection (explicit Unicode ranges for Hiragana, CJK ideographs, and Hangul) is handled internally so most consumers never need to think about it. The ReadingTimeStream class extends Node’s Transform in object mode, giving the same counting logic a streaming interface with almost no additional API surface to learn.