dayjs, js-joda, luxon, and moment are JavaScript libraries designed to parse, validate, manipulate, and display dates and times. While moment was the industry standard for years, it is now in maintenance mode. luxon serves as its modern successor by the same author, focusing on immutability and native Intl API usage. dayjs offers a lightweight, plugin-based alternative with a similar API to moment. js-joda provides a strict, immutable port of the Java ThreeTenBp library for developers who need robust domain modeling.
Handling dates and times in JavaScript has historically been painful. The native Date object is mutable, confusing, and lacks time zone support. To solve this, the community built libraries like moment, which became the standard for over a decade. However, the ecosystem has evolved. Today, we have dayjs, luxon, and js-joda offering different approaches to the same problem. Let's break down how they work and which one fits your architecture.
Before comparing features, we must address the elephant in the room. moment is no longer under active development.
moment is in maintenance mode.
// moment: Legacy usage
import moment from 'moment';
const now = moment(); // Works, but library is frozen in time
luxon, dayjs, and js-joda are actively maintained.
// luxon: Actively maintained
import { DateTime } from 'luxon';
const now = DateTime.now();
// dayjs: Actively maintained
import dayjs from 'dayjs';
const now = dayjs();
// js-joda: Actively maintained
import { LocalDate } from '@js-joda/core';
const now = LocalDate.now();
How the library handles data changes is the biggest technical difference. This affects how you write logic and avoid bugs.
moment is mutable.
.add() changes the original object..clone() to keep the original safe.// moment: Mutable
const date = moment('2023-01-01');
const nextDay = date.add(1, 'day');
// 'date' is now also '2023-01-02' because it was modified in place
luxon is immutable.
.plus() return a new instance.// luxon: Immutable
const date = DateTime.fromISO('2023-01-01');
const nextDay = date.plus({ days: 1 });
// 'date' remains '2023-01-01', 'nextDay' is '2023-01-02'
dayjs is immutable.
luxon, operations return new instances.moment API style but fixes the mutability issue.moment syntax without the bugs.// dayjs: Immutable
const date = dayjs('2023-01-01');
const nextDay = date.add(1, 'day');
// 'date' remains '2023-01-01', 'nextDay' is '2023-01-02'
js-joda is strictly immutable.
java.time.// js-joda: Strictly Immutable
const date = LocalDate.parse('2023-01-01');
const nextDay = date.plusDays(1);
// 'date' remains '2023-01-01', 'nextDay' is '2023-01-02'
Timezones are notoriously difficult. Libraries handle this differently depending on their reliance on browser APIs.
luxon uses the native Intl API.
Intl.DateTimeFormat under the hood.// luxon: Native Intl support
const dt = DateTime.now().setZone('America/New_York');
console.log(dt.zoneName); // 'America/New_York'
dayjs uses a plugin system.
timezone plugin.// dayjs: Plugin-based timezone
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
const dt = dayjs().tz('America/New_York');
moment uses a heavy plugin.
moment-timezone which includes a large data file.// moment: Heavy timezone plugin
import moment from 'moment-timezone';
const dt = moment.tz('2023-01-01', 'America/New_York');
js-joda includes timezone support in core/extra.
ZonedDateTime for full timezone awareness.// js-joda: Explicit timezone classes
import { ZonedDateTime, ZoneId } from '@js-joda/core';
import '@js-joda/timezone'; // Required for zone data
const dt = ZonedDateTime.now(ZoneId.of('America/New_York'));
Developer experience often comes down to how easy it is to read and write dates.
moment uses tokens like YYYY-MM-DD.
// moment: Token-based formatting
moment().format('YYYY-MM-DD HH:mm');
luxon uses Unicode tokens (like yyyy-MM-dd).
moment, requiring a mental shift.// luxon: Unicode tokens
DateTime.now().toFormat('yyyy-MM-dd HH:mm');
dayjs uses moment-style tokens.
moment formatting strings.moment code.// dayjs: Moment-style tokens
dayjs().format('YYYY-MM-DD HH:mm');
js-joda uses formatters.
// js-joda: Formatter objects
import { DateTimeFormatter } from '@js-joda/core';
const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm');
const str = LocalDate.now().format(formatter);
Despite their differences, these libraries solve the same core problems.
// moment
moment('2023-10-05T14:48:00');
// dayjs
dayjs('2023-10-05T14:48:00');
// luxon
DateTime.fromISO('2023-10-05T14:48:00');
// js-joda
LocalDate.parse('2023-10-05');
// moment
moment().add(1, 'month');
// dayjs
dayjs().add(1, 'month');
// luxon
DateTime.now().plus({ months: 1 });
// js-joda
LocalDate.now().plusMonths(1);
// moment
moment('invalid').isValid(); // false
// dayjs
dayjs('invalid').isValid(); // false
// luxon
DateTime.fromISO('invalid').isValid; // false
// js-joda
// Throws exception or requires parsing check
try { LocalDate.parse('invalid'); } catch(e) { /* invalid */ }
| Feature | dayjs | js-joda | luxon | moment |
|---|---|---|---|---|
| Status | โ Active | โ Active | โ Active | โ ๏ธ Maintenance |
| Mutability | Immutable | Immutable | Immutable | Mutable |
| Bundle Size | ๐ชถ Very Small | ๐ฆ Medium | ๐ฆ Medium | ๐ Large |
| Timezones | Plugin (Bundled) | Module (Explicit) | Native (Intl) | Plugin (Heavy) |
| API Style | Chainable | Functional/Static | Static/Chainable | Chainable |
| Migration | Easy from Moment | Hard (New Pattern) | Medium (New Tokens) | N/A |
dayjs is the lightweight champion ๐. If you loved moment but hate the bundle size, this is your pick. It feels familiar, works everywhere, and stays small thanks to its plugin architecture.
luxon is the modern standard ๐. Built by the creator of moment, it fixes the design flaws of the past. If you want robust timezone support without extra data files and don't mind learning new formatting tokens, choose this.
js-joda is the strict engineer ๐ง. If you come from a Java background or need rock-solid immutability for complex domain logic, this library provides the most rigorous structure.
moment is the legacy debt โ ๏ธ. It built the foundation for modern date handling, but its time has passed. Do not start new projects with it.
Final Thought: For most modern frontend applications, luxon offers the best balance of correctness and native API usage. However, if bundle size is your top constraint or you need a direct moment replacement, dayjs is an excellent choice. Avoid moment unless you are maintaining old code.
Choose dayjs if you need a lightweight library with a familiar API similar to moment but with a smaller footprint. It is ideal for frontend projects where bundle size matters and you need extensibility through plugins without the overhead of a large core library.
Choose js-joda if you require strict immutability and a domain-driven approach to date time handling. It is suitable for complex business logic where date calculations must be precise, thread-safe (in concept), and free from side effects, often mirroring Java backend systems.
Choose luxon if you want a modern, immutable library that leverages the native Intl API for timezone and locale support. It is the recommended upgrade path for teams moving away from moment, offering robust features without needing polyfills for modern environments.
Do NOT choose moment for new projects. It is officially in maintenance mode and no longer receives feature updates. Only use it if you are maintaining a legacy codebase that cannot be refactored immediately, and plan to migrate to luxon or dayjs as soon as possible.
English | ็ฎไฝไธญๆ | ๆฅๆฌ่ช | Portuguรชs Brasileiro | ํ๊ตญ์ด | Espaรฑol (Espaรฑa) | ะ ัััะบะธะน | Tรผrkรงe | เทเทเถเทเถฝ | ืขืืจืืช
Fast 2kB alternative to Moment.js with the same modern API
Day.js is a minimalist JavaScript library that parses, validates, manipulates, and displays dates and times for modern browsers with a largely Moment.js-compatible API. If you use Moment.js, you already know how to use Day.js.
dayjs().startOf('month').add(1, 'day').set('year', 2018).format('YYYY-MM-DD HH:mm:ss');
You can find more details, API, and other docs on day.js.org website.
npm install dayjs --save
It's easy to use Day.js APIs to parse, validate, manipulate, and display dates and times.
dayjs('2018-08-08') // parse
dayjs().format('{YYYY} MM-DDTHH:mm:ss SSS [Z] A') // display
dayjs().set('month', 3).month() // get & set
dayjs().add(1, 'year') // manipulate
dayjs().isBefore(dayjs()) // query
๐API Reference
Day.js has great support for internationalization.
But none of them will be included in your build unless you use them.
import 'dayjs/locale/es' // load on demand
dayjs.locale('es') // use Spanish locale globally
dayjs('2018-05-05').locale('zh-cn').format() // use Chinese Simplified locale in a specific instance
A plugin is an independent module that can be added to Day.js to extend functionality or add new features.
import advancedFormat from 'dayjs/plugin/advancedFormat' // load on demand
dayjs.extend(advancedFormat) // use plugin
dayjs().format('Q Do k kk X x') // more available formats
๐Plugin List

Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
[Become a sponsor via Github] [Become a sponsor via OpenCollective]
ย ย ย ย ย ย ย ย
ย ย ย ย ย ย ย ย
ย ย ย ย ย ย ย ย
ย ย ย ย ย ย ย ย
This project exists thanks to all the people who contribute.
Please give us a ๐ star ๐ to support us. Thank you.
And thank you to all our backers! ๐
Day.js is licensed under a MIT License.