Library / SDK
formkit/tempo avatar
formkit/tempo

formkit/tempo: a thin utility layer over Intl.DateTimeFormat

📆 Parse, format, manipulate, and internationalize dates and times in JavaScript and TypeScript.

2,585 stars38 forksTypeScriptMIT

At a glance

What is it?
Tempo is a small TypeScript date library that keeps the native Date object and mines Intl.DateTimeFormat for timezone offsets and locale-aware formats. This review covers what it does, how it installs, and where it stops being the right choice.
Who is it for?
Adopt Tempo if you want date formatting, parsing and timezone work on top of the native Date object and you care about bundle size, since the project states that timezone work costs a few bytes of library code and full international formatting about 2Kb minified and brotlied. Do not adopt it if you need a custom immutable date primitive, because the README is explicit that Tempo is a collection of utilities for working with Date objects.
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 92 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 October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Who Tempo is for and what it replaces

The README places Tempo in the tradition of moment.js, day.js and date-fns, and names the goal directly: to be as small and easy to use as possible. The distinction it draws is architectural. Other libraries in that lineage hand you a custom date primitive, a wrapper object you pass around instead of a Date. Tempo does not. The README describes it as a collection of utilities for working with Date objects. If your code already stores dates as Date, passes them across module boundaries, and serializes them to JSON, that choice means adoption is mostly about calling functions rather than converting types at the edges. It also means you inherit every quirk of the built-in Date, including its mutable setter methods and its parsing behaviour, because Tempo is not shielding you from the platform. The audience is JavaScript and TypeScript developers who need formatting, parsing, manipulation and timezone conversion, and who would rather not add a date class to their public API surface.

Tempo's mechanism: Intl.DateTimeFormat as the data source

The README states that under the hood Tempo mines JavaScript's Intl.DateTimeFormat to extract complex data such as timezone offsets and locale-aware date formats, then exposes a simpler API on top. That is the whole design in one sentence. Instead of bundling timezone tables or locale pattern strings, Tempo asks the runtime's internationalization engine what it already knows, and derives offsets and format patterns from the answer. The practical consequence is that correctness for a given locale or timezone depends on the host environment's ICU data, not on the library's release cycle. Node built with full ICU and a modern browser will answer differently from a stripped-down runtime. The second consequence is size. Because the heavy data is not shipped, the README says you can work with timezones with only a few bytes of library code, or perform full international date formatting for roughly 2Kb minified and brotlied. The project uses Size Limit, listed in devDependencies as @size-limit/preset-small-lib and related packages, to keep that budget honest, and package.json defines a size script that runs the build and then size-limit. The repository is a pnpm workspace built with tsup, and the test script pins the timezone: TZ="America/New_York" vitest. Pinning the timezone in the test command is a small signal that the maintainers treat environment-dependent output as something to control rather than assume.

Installing and building Tempo from the repository root

The README's install instructions are for developing the library itself, not for adding it to an application. It says to install from the repository root with pnpm, and it starts with corepack so the pinned package manager is available.

bash
corepack enable
pnpm install

After that, the README gives two build commands: pnpm build for the library and pnpm docs-build for the docs app.

bash
pnpm build
bash
pnpm docs-build

What you should see is the compiled output in dist, plus a post:build step that invokes the size-limit script and falls back to the message 'Size limit check skipped' if that check cannot run. The published package is named @formkit/tempo in package.json, and its exports map points import at dist/index.mjs and require at dist/index.cjs, with a separate browser bundle at dist/bundle.mjs. The package is type module, so the ESM entry is the primary one. What the README does not give is a consumer-facing install line or a first-use code example. It points to the documentation site at tempo.formkit.com instead, and the repository layout reflects that: there is a docs directory and a scripts directory alongside src. Anyone evaluating the API surface has to go to the docs site, because the README alone does not document the exported function signatures, the format token table, or the parsing options.

The trade-off in keeping the native Date object

Tempo's central promise is also its main limitation. By returning and accepting Date objects, it cannot fix the things people dislike about Date. A Date is mutable, so a function that returns one gives the caller the ability to change it in place, and nothing in the library's design prevents that. A Date holds a single instant with no attached timezone, so any timezone context has to travel alongside it in your own code. Libraries built on a custom primitive can attach that context to the value itself. Tempo cannot, and the README does not claim otherwise. The second limitation follows from the Intl dependency. If a runtime lacks the ICU data for a locale, the formatting you get back is whatever that runtime falls back to, and Tempo has no bundled table to correct it. That is the price of the small bundle. The third is documentation depth in the repository itself. The README is short and points to tempo.formkit.com for the real content, so the repository alone is not enough to evaluate the API surface. Anyone deciding on adoption should read the docs site, not the README, and should check the exported types in dist/index.d.ts for the installed version.

Tempo compared with date-fns and day.js

The README names date-fns and day.js as the tradition Tempo belongs to, so the comparison is fair game. date-fns is also a collection of functions rather than a wrapper class, which makes it the closest match in shape. The difference the README points at is where the locale and timezone data comes from: Tempo derives it from Intl.DateTimeFormat, while the older libraries have historically carried their own locale data. That shifts the size and the correctness question onto the runtime. day.js takes the opposite approach from both, wrapping dates in its own immutable object with a chainable API and plugins for timezones and locales. If you want immutability enforced by the type system, day.js gives you that and Tempo does not. If you want to keep passing plain Date objects through existing code, Tempo's approach avoids a conversion layer at every boundary. moment.js is in the same lineage but the README treats it as an ancestor rather than a peer, and the project does not position itself as a drop-in replacement for any of them.

Maintenance, licence and upgrade cost

The repository is not archived, and the last push was on 2026-07-01. The release history shows v0.1.2 in June 2024, v1.0.0 in December 2025, and v1.1.0 in May 2026, so the cadence has been uneven rather than steady, with a long gap before the 1.0 line. The package is licensed MIT, which permits commercial use, modification and redistribution provided the copyright notice and licence text are retained. That is a permissive licence and the practical obligation is attribution, but the repository's LICENSE file is the authority and this is not legal advice. Upgrade cost looks low for the 1.x line: the package ships both ESM and CJS entry points and a browser bundle, so consumers on either module system have a path. The risk to watch on upgrade is the size budget. Because the project enforces limits with Size Limit, a version bump that adds exports can change your bundle if you import from the root rather than from specific paths. The repository layout includes a docs directory and a scripts directory alongside src, and package.json publishes only dist plus two images, so the install footprint is the compiled output rather than the source tree.

Editorial conclusion

Adopt Tempo if you want date formatting, parsing and timezone work on top of the native Date object and you care about bundle size, since the project states that timezone work costs a few bytes of library code and full international formatting about 2Kb minified and brotlied. Do not adopt it if you need a custom immutable date primitive, because the README is explicit that Tempo is a collection of utilities for working with Date objects. Before committing, verify the exact export names in the published dist/index.d.ts for the version you install, and run the size check yourself with the size script in package.json.

Frequently asked questions

What is @formkit/tempo?

It is a JavaScript and TypeScript library for parsing, formatting, manipulating and internationalizing dates and times. The README describes it as a collection of utilities for working with Date objects rather than a library that provides its own date primitive.

How do I install @formkit/tempo?

The package is published to npm as @formkit/tempo. The install commands in the README are for developing the repository itself, where you run corepack enable and then pnpm install from the repository root.

Does @formkit/tempo ship its own timezone and locale data?

No. The README states that Tempo mines JavaScript's Intl.DateTimeFormat to extract timezone offsets and locale-aware date formats, so that data comes from the runtime rather than from the library.

How large is @formkit/tempo in a bundle?

The README says you can work with timezones with only a few bytes of library code, or perform full international date formatting for roughly 2Kb minified and brotlied. The project controls this with Size Limit.

What licence does @formkit/tempo use?

The package is licensed MIT, which allows commercial use and modification as long as the copyright notice and licence text are kept. The LICENSE file in the repository is the authoritative text.

Official sources

  1. formkit/tempo 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/formkit-tempo.svg)](https://hysenlabs.com/projects/formkit-tempo)