mrkeyoor.com_
Sat 19 Sept 15:50 UTC
npmUtilsupdated 19 Sept 2026

dayjs review

Our sandbox build of Day.js 1.11.23 was 7.5 KB minified and 3.3 KB gzipped, which is the main reason to consider this Moment-style date wrapper. It wraps JavaScript `Date` in immutable, chainable values for parsing, formatting, comparisons, and calendar arithmetic. Strict formats, UTC, named zones, durations, relative text, and many comparison helpers arrive as plugins that must be registered with `dayjs.extend`. UTC and named-zone calls return the same `Dayjs` TypeScript type as local values; the duration plugin supplies a separate `Duration` type. The current release is a narrow timezone fix: invalid zoned input now stays an invalid Day.js value instead of causing `Intl.DateTimeFormat` to throw `RangeError`.

64.7Mdownloads / wk
Verdict

Our Day.js 1.11.23 install finished in 1.4 seconds, left 1 package using 3 MB, and produced a 3.3 KB gzipped browser bundle with 0 audit findings. Install it for Moment-shaped formatting and calendar math when startup plugin registration and a single Date-backed value type are acceptable.

We installed it

Lab card: what happened when we installed dayjsScreenshot of dayjs documentation
Install✓ · 1.4s1 package on disk · 3 MB
ImportESM import works · require() works · CommonJS package
Browser3.3 KBgzipped (7.5 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does dayjs install cleanly?

Yes. In a fresh container with an empty cache, npm install dayjs finished in 1 seconds, leaving 1 package and 3 MB on disk. npm audit reported no known vulnerabilities.

How much does dayjs add to a browser bundle?

3.3 KB gzipped (7.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does dayjs work with both ESM and CommonJS?

Yes. Both import 'dayjs' and require('dayjs') worked in Node 22 in our run. The package is published as CommonJS.

Does dayjs include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

dayjs or date-fns: which should you use?

date-fns: Choose it when named function imports and native Date objects fit better than a wrapper with registered methods. Our Day.js 1.11.23 install finished in 1.4 seconds, left 1 package using 3 MB, and produced a 3.3 KB gzipped browser bundle with 0 audit findings.

When should you not use dayjs?

Your domain types must prevent a plain date from being passed where an instant is expected. The bundled declarations return the same Dayjs class from core, UTC, and timezone calls.

API stability5/5Day.js 1.11.23 keeps the established 1.x shape: `dayjs()` creates one immutable value, familiar methods return another value, and `extend()` installs optional APIs. The latest release changes only invalid-input handling in the timezone plugin, where an invalid value is returned in place of a `RangeError`. Plugin declaration files continue to augment the same `Dayjs` interface. Calls have stayed steady, although the methods available at runtime still depend on startup registration.
Docs4/5The official installation URL currently returns HTTP 200. Day.js documents core parsing, strict formats, arithmetic, differences, locales, UTC, named zones, durations, and each plugin on focused pages with small examples. The gaps appear when assembling a production policy: plugin order, global locale effects, host `Intl` data, default-zone differences from Moment, and permissive validation sit on separate pages rather than in one setup guide.
Maintenance4/5GitHub reports an unarchived repository pushed on September 1, 2026, with 1,325 open issues and pull requests. Four 1.11 releases shipped during 2026. Version 1.11.22 corrected timezone offset calculation when the host has different daylight-saving rules, and 1.11.23 followed with invalid-value handling. The fix stream is active, but the four-digit queue means an uncommon edge case may wait for attention.
Ecosystem5/5npm counted 70,087,712 downloads for the latest completed week, and GitHub showed 48,662 stars. The license is MIT. The package includes TypeScript declarations, locale modules, and plugins for UTC, named zones, durations, relative labels, ISO weeks, and range checks. Our install found 0 direct dependencies and working CommonJS and ESM loading. Teams still have to agree on one plugin and locale bootstrap so entry points expose the same methods.

Discussed on

  1. hnShow HN: Fast 2kB date library alternative to Moment.js with the same API122 points
  2. hnShow HN: Day.js 1.6 locale and plugins 2kb moment.js alternative with same API6 points
  3. hnShow HN: I made a React/DayJS picker as my first open source library5 points
  4. hnAsk HN: The Day.js Dilemma: How Should We Handle OSS Maintainers Going MIA?4 points

Use it if

  • You are replacing Moment call sites and want to retain familiar tokens, method names, and chains while making date operations immutable.
  • A browser feature needs parsing, formatting, comparisons, and calendar math within the 3.3 KB gzipped bundle we measured.
  • Your application has one startup path where every required plugin and locale can be registered before request or UI code runs.
  • Each supported runtime has `Intl` timezone data you can test, and shipping a separate IANA database is unnecessary.
Skip it if

Setup reality

In our measurement setup, Day.js 1.11.23 installed in 1.4 seconds inside an unprivileged Node 22 Bookworm container with 3 CPUs, 8 GB RAM, and no cache. It left 1 package using 3 MB. The package was 2,136 KB unpacked, with 0 direct dependencies and 0 peers. npm audit reported 0 vulnerabilities at every severity. TypeScript declarations were bundled, and both require() and ESM import worked even though the package is CommonJS and has no exports map.

Day.js 1.11.23 asks for no credentials and reads no config file. Setup lives in code: import each plugin and pass it to dayjs.extend() before a caller touches the added method, then import every non-English locale you select. The declaration files use module augmentation, so TypeScript can accept .tz() even when a process forgot runtime registration. Put one bootstrap module ahead of application code and load it in tests too.

Core validation is permissive: the docs show 2022-01-33 rolling into February and passing isValid(). Register customParseFormat and pass true for form input. Assign results from add, subtract, set, and startOf because the original object does not change. diff returns an integer by default and keeps decimals only when its third argument is true. Moment-shaped method names can hide these different rules during a migration.

Named-zone work needs utc registered before timezone and relies on host Intl data. The plugin caches Intl.DateTimeFormat objects per zone and display-name choice; it has no IANA file to refresh. Version 1.11.23 turns invalid zoned parsing into an invalid value instead of a RangeError, so callers still need isValid(). Our esbuild browser build of a package-namespace import measured 7.5 KB minified and 3.3 KB gzipped. Re-measure after adding the plugins and locales used by the real entry point.

Patterns

Parse an ISO instant and format it parse-iso-timestamp

import dayjs from 'dayjs';

const createdAt = dayjs('2026-09-07T08:15:00Z');
if (!createdAt.isValid()) throw new Error('invalid timestamp');

console.log(createdAt.format('YYYY-MM-DD [at] HH:mm Z'));

An ISO string ending in `Z` names an instant, but `format()` renders it in the process's local zone. Use UTC or timezone mode when output must ignore that zone.

Reject an impossible form date strict-form-parse

import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';

dayjs.extend(customParseFormat);

const birthday = dayjs('29/02/2025', 'DD/MM/YYYY', true);
if (!birthday.isValid()) throw new Error('invalid birthday');

The final `true` requires an exact, valid match. Without `customParseFormat`, the named layout is unavailable.

Add calendar time without changing the source immutable-calendar-math

import dayjs from 'dayjs';

const orderedAt = dayjs('2026-09-07T09:00:00');
const dueAt = orderedAt.add(7, 'day').add(2, 'hour');

console.log(orderedAt.format());
console.log(dueAt.format());

Each `add()` returns a new Day.js object. Code that ignores the return value also loses the calculation.

Build a half-open calendar-month range build-month-range

import dayjs from 'dayjs';

const value = dayjs('2026-09-18T10:15:00');
const from = value.startOf('month');
const toExclusive = value.add(1, 'month').startOf('month');

console.log({ from: from.toISOString(), toExclusive: toExclusive.toISOString() });

`startOf()` returns a clone. An exclusive next-month boundary avoids depending on the final millisecond returned by `endOf('month')`.

Keep or discard fractional differences calculate-difference

import dayjs from 'dayjs';

const startedAt = dayjs('2026-01-01');
const checkedAt = dayjs('2026-09-07');

const wholeMonths = checkedAt.diff(startedAt, 'month');
const fractionalMonths = checkedAt.diff(startedAt, 'month', true);

A two-argument `diff()` truncates to an integer. Passing `true` as the third argument keeps the fractional result.

Check an inclusive day range check-inclusive-range

import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween';

dayjs.extend(isBetween);

const value = dayjs('2026-09-07');
const inside = value.isBetween('2026-09-01', '2026-09-07', 'day', '[]');

The `[]` marker includes both boundaries. Leaving it out makes the range exclusive at both ends.

Keep an instant in UTC mode parse-and-serialize-utc

import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';

dayjs.extend(utc);

const receivedAt = dayjs.utc('2026-09-07T08:15:00Z');
console.log(receivedAt.format('YYYY-MM-DD HH:mm [UTC]'));
console.log(receivedAt.toISOString());

`dayjs.utc()` makes getters and formatting use UTC. Core `dayjs()` uses the host's local zone for offset-free input.

Parse a wall time in a named zone parse-named-timezone

import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(customParseFormat);
dayjs.extend(utc);
dayjs.extend(timezone);

const opensAt = dayjs.tz(
  '07/09/2026 09:30',
  'DD/MM/YYYY HH:mm',
  'Europe/London',
);
if (!opensAt.isValid()) throw new Error('invalid opening time');

The timezone plugin depends on `utc`, and the explicit layout depends on `customParseFormat`. In 1.11.23, a bad zoned value stays invalid instead of throwing `RangeError`.

Show one instant in another zone convert-timezone

import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';

dayjs.extend(utc);
dayjs.extend(timezone);

const instant = dayjs.utc('2026-09-07T08:15:00Z');
const kolkata = instant.tz('Asia/Kolkata');
console.log(kolkata.format('YYYY-MM-DD HH:mm Z'));

`.tz(zone)` preserves the instant and changes its display. Passing `true` as the second argument preserves wall-clock fields and changes the instant.

Describe one date relative to another format-relative-time

import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';

dayjs.extend(relativeTime);

const publishedAt = dayjs('2026-09-04');
const viewedAt = dayjs('2026-09-07');
console.log(publishedAt.from(viewedAt));

`from()` and `fromNow()` exist only after registering `relativeTime`. Their wording follows the active locale.

Format one value in French localize-one-value

import dayjs from 'dayjs';
import 'dayjs/locale/fr';

const label = dayjs('2026-09-07')
  .locale('fr')
  .format('dddd D MMMM YYYY');

console.log(label);

The `fr` module must be imported before selection. Instance-level `locale()` returns a new value and leaves the global default unchanged.

Add a duration object to an instant create-and-apply-duration

import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';

dayjs.extend(duration);

const retryDelay = dayjs.duration({ minutes: 2, seconds: 30 });
const retryAt = dayjs('2026-09-07T08:15:00Z').add(retryDelay);

console.log(retryDelay.asSeconds());
console.log(retryAt.toISOString());

`dayjs.duration()` and duration-aware `add()` come from the duration plugin; neither call is available in core.

Alternatives

PackageRegistryPick it when
date-fnsnpmChoose it when named function imports and native `Date` objects fit better than a wrapper with registered methods.
luxonnpmChoose it when zones, intervals, and durations should be explicit objects in the main API.
@js-temporal/polyfillnpmChoose it when code should distinguish instants, plain dates, wall-clock values, and zoned date-times by type.

More utils guides

lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.