Library / SDK
hustcc/timeago.js avatar
hustcc/timeago.js

timeago.js: a 2 kB relative-time formatter for browser and Node

:clock8: :hourglass: timeago.js is a tiny(2.0 kb) library used to format date with `*** time ago` statement.

5,368 stars402 forksTypeScriptMIT

At a glance

What is it?
timeago.js turns a timestamp into strings like '3 hours ago' or 'in 12 seconds', with i18n and live DOM rendering. It is small and focused, but its last npm release predates the current source by several years.
Who is it for?
Adopt timeago.js when you need one narrow thing: turning timestamps into localized relative strings in a browser or Node process, without pulling in a full date framework. Skip it if you need calendar arithmetic, time zones, duration math or parsing beyond what Date accepts, because the library does none of that.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 93 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The narrow job timeago.js does, and who has it

Most date libraries answer the question 'what date is this'. timeago.js answers a different one: 'how long ago was this, in words'. The README describes it as a nano library of less than 2 kb used to format datetime with a `*** time ago` statement, and the examples it gives are exactly that: 'just now', '12 seconds ago', '2 hours ago', '3 days ago', '3 weeks ago', '2 years ago', plus the forward-looking forms 'in 12 seconds', 'in 24 days', 'in 6 months'.

That scope is the whole product. There is no calendar view, no time zone conversion, no duration arithmetic, no parsing of natural-language input. If your interface shows a comment feed, an activity log, a notification list or a changelog where every row carries a timestamp, this is the shape of library you want: the timestamp is already known, only its presentation is in question.

The audience is correspondingly narrow. Front-end developers rendering lists of dated items, and Node developers producing the same strings server-side, are the two groups the README names directly with its 'Node and browser supported' bullet. Anyone who needs date math should look elsewhere; that is not a limitation of the implementation so much as a boundary of the design.

Four APIs and a locale function

The public surface is small enough to hold in your head. The README states there are only four APIs: `format`, `render`, `cancel` and `register`. `format(date[, locale = 'en_US', opts])` takes a Date instance, a timestamp or a date string and returns the relative string. `render(dom[, locale = 'en_US', opts])` and `cancel([dom])` handle live updating of DOM nodes. `register(locale, localeFunc)` adds a language.

The mechanism behind `render` is the interesting part. Elements are marked with a `datetime` attribute, and `render` finds them and rewrites their text as time passes. The README's example is `<div class="timeago" datetime="2016-06-30 09:20:00"></div>`. Because the update runs on a timer rather than on every frame, the `opts` type exposes `minInterval`, described in the source types as 'the realtime min update interval'. That is the throttle: you decide how often the text is allowed to change, and the README shows `{ minInterval: 3 }` as an example value. `cancel()` stops all pending tasks, and passing a node cancels just that one.

Localization is a function, not a data file you load. A `localeFunc` receives `(number, index, totalSec)` and returns a pair of strings, the past form and the future form, for that index. The README's example array runs from 'just now' through '%s years ago', with placeholders like `%s` filled by the number. Only `en_US` and `zh_CN` are built in; the README points at `src/lang` for the rest and invites pull requests for new translations.

Installing timeago.js and formatting your first timestamp

The README gives one install command. Run it in your project root and the package lands in node_modules as `timeago.js`.

bash
npm install timeago.js

Import the named exports. The package is TypeScript, and package.json points `types` at `dist/index.d.ts`, so the editor picks up the signatures without a separate types package.

ts
import { format, render, cancel, register } from 'timeago.js';

A first real call takes a millisecond timestamp. The README's own example subtracts eleven hours from the current clock and expects '11 hours ago' back.

ts
import { format } from 'timeago.js';

format(Date.now() - 11 * 1000 * 60 * 60); // returns '11 hours ago'
format('2018-12-12');
format(1544666010224, 'zh_CN');

The second argument is the locale, defaulting to `en_US`. To keep a page of timestamps current without re-rendering your own components, mark the elements and hand them to `render`.

ts
import { render, cancel } from 'timeago.js';

const nodes = document.querySelectorAll('.timeago');
render(nodes, 'zh_CN');
cancel(nodes[0]);

If you would rather not bundle it, the README offers a CDN path: a script tag pointing at `//unpkg.com/timeago.js` exposes a global variable named `timeago`. There is also a prebuilt `dist/timeago.min.js` for a plain script tag.

Where timeago.js stops being the right tool

The library formats; it does not compute. Feed it a string and it relies on the platform's own Date parsing, which means a format your runtime does not accept will fail before timeago.js is involved at all. The README does not document a fallback for unparseable input, and it does not document how invalid dates are rendered.

The `render` timer is the second constraint. It rewrites DOM text on an interval, and the only control the documented `opts` type exposes is `minInterval`. There is no documented pause when a tab is hidden, no documented visibility handling, and no documented integration with any component lifecycle. In a single-page application you own that: call `cancel` when the component unmounts, or the task keeps running against detached nodes. The README shows `cancel()` with no argument clearing all tasks, which is blunt but available.

Localization is the third boundary. Two locales ship built in. Every other language is a `localeFunc` you register yourself, and writing one means mapping fourteen indices to past and future strings, then testing it. The README explicitly asks contributors to run `npm test` when adding a locale, which tells you the maintainers treat translation correctness as something that needs verification rather than assumption. Plural rules that do not fit the English one/other pattern are not addressed by the documented function signature.

Finally, consider the release cadence. The most recent release listed on the repository is v4.0.2, dated 2019-12-21. package.json declares version 4.1.0, and the source tree has moved to a Vite-based build with separate UMD and full bundles. The last push to the repository was on 2026-06-30. That combination, an old published tag and newer unreleased source, is worth understanding before you depend on a specific behaviour.

timeago.js against a full date library

The honest alternative is not another relative-time formatter. It is a general date library such as Moment.js, or one of the modern replacements people moved to after Moment, which handle formatting, parsing, time zones and arithmetic in one dependency. The difference in approach is scope. A full date library owns your entire date story: you pass it a date, ask for a format string or a locale-aware output, and it also parses, mutates and converts. timeago.js owns one output shape and nothing else.

That shows up in the numbers. The README's headline claim is under 2 kb, and the package ships a UMD build at `dist/timeago.min.js` specifically so a script tag can carry it. A general date library with locale data is orders of magnitude larger once you include the locales you actually use. If your only need is '3 hours ago', paying for a full date framework is a poor trade.

The trade runs the other way too. With a full date library you get one dependency, one mental model and one upgrade path. With timeago.js you get a small formatter plus whatever you already use for parsing and time zones, which may be the platform's Date, a date library, or a server-side serializer. Two dependencies instead of one. The README even credits its inspiration: the official website is based on rmm5t/jquery-timeago, described there as a nice and featured project that depends on jQuery. Dropping the jQuery dependency is precisely the gap timeago.js was built to fill.

Licence, releases and what an upgrade actually costs

The licence is MIT, attributed to hustcc in both the README and package.json. For most teams that is the permissive case: you can use it commercially, and the obligation is essentially to keep the copyright and permission notice with the code. That is a description of the licence text, not legal advice; if your organisation has a policy on third-party notices, run it past whoever owns that policy.

The upgrade picture is the part worth slowing down for. The repository lists v4.0.2 as the newest release, published 2019-12-21, while package.json declares 4.1.0. Anyone installing from npm should confirm which version resolves before assuming the source they read on the default branch matches the artifact they ship. The build pipeline has clearly changed since that release: package.json drives Vite configs for the library, UMD and full builds, plus separate CommonJS and ESM compilation steps through tsc, and `prepublishOnly` runs the full build. The `exports` map defines `import` and `require` conditions plus subpath exports under `./lib/*` and `./esm/*`.

For consumers the practical cost is low, because the API is four functions and the types ship with the package. A major version bump would most likely touch the `Opts` type or the `exports` map rather than the call signatures. The expensive part of any upgrade here is not the code change; it is re-verifying locale output across the languages you registered, since those strings are user-visible and a silent change to one index changes what every user of that locale reads.

Editorial conclusion

Adopt timeago.js when you need one narrow thing: turning timestamps into localized relative strings in a browser or Node process, without pulling in a full date framework. Skip it if you need calendar arithmetic, time zones, duration math or parsing beyond what Date accepts, because the library does none of that. Before wiring it in, check three things: that the locale you need exists under src/lang or that you are ready to write a localeFunc and register it, that render and cancel fit your framework's unmount path, and which version you are actually installing, since package.json declares 4.1.0 while the newest release listed on the repository is v4.0.2 from 2019-12-21.

Frequently asked questions

Is Moment.js deprecated, and does that affect timeago.js?

timeago.js does not depend on Moment.js, so a deprecation there does not change how timeago.js works. The comparison is only about scope: a full date library handles parsing, formatting and arithmetic, while timeago.js produces relative strings such as '3 hours ago' and nothing else.

What is the best JavaScript date library?

The README does not rank libraries. It positions timeago.js narrowly: a sub-2 kb library that formats datetime with a `*** time ago` statement, with `en_US` and `zh_CN` built in and other locales added through `register`. If your need is only relative time, that narrow scope is the point.

How do I get a timestamp in JavaScript to pass to timeago.js?

The README's `format` API accepts a Date instance, a timestamp or a date string, so `Date.now()` works directly. Its own example passes `Date.now() - 11 * 1000 * 60 * 60` and expects '11 hours ago' back.

Official sources

  1. hustcc/timeago.js on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hustcc-timeago-js.svg)](https://hysenlabs.com/projects/hustcc-timeago-js)