Migrating from moment.js - Complete Guide

moment.js is in maintenance mode. It still works and 2.29.4 has no unpatched CVEs, but new features will not be added and future security fixes are not guaranteed. This guide covers migrating to dayjs (near drop-in) or date-fns (more modern, tree-shakeable).

moment.js Status

moment.js has been in maintenance-only mode since September 2020. No new features, no non-security bug fixes. It has 4 known CVEs - all fixed in 2.29.4. Future CVEs may not receive patches.

Choose your replacement

Easiest migration

dayjs

Near-identical API to moment.js. 2KB gzipped vs moment 67KB. Most moment.js code works after changing the import.

npm install dayjs

Most modern

date-fns

Functional API - completely different from moment. Tree-shakeable, TypeScript-first. Better for new code than migration.

npm install date-fns

Full featured

Luxon

Built by a moment.js author. Wraps the native Intl API. Immutable by default. Best for serious timezone and locale support.

npm install luxon

No dependency (Node 18+)

Temporal API

Upcoming native JavaScript date API. Currently a TC39 proposal with polyfill available.

npm install @js-temporal/polyfill

Option A - Migrate to dayjs (recommended for existing codebases)

Step 1 - Install dayjs and remove moment

npm install dayjs
npm uninstall moment

Step 2 - Replace imports

moment.js
import moment from 'moment';
dayjs
import dayjs from 'dayjs';

Step 3 - Basic usage

moment.js
// Parse
moment('2024-01-15')
moment('2024-01-15', 'YYYY-MM-DD')

// Format
moment().format('YYYY-MM-DD')
moment().format('MMMM Do YYYY')

// Manipulate
moment().add(7, 'days')
moment().subtract(1, 'month')

// Compare
moment('2024-01-15').isBefore('2024-06-01')
moment('2024-01-15').isAfter(moment())

// Display
moment().fromNow()
moment().toDate()
dayjs
// Parse
dayjs('2024-01-15')
dayjs('2024-01-15', 'YYYY-MM-DD')  // needs customParseFormat plugin

// Format
dayjs().format('YYYY-MM-DD')
dayjs().format('MMMM Do YYYY')     // needs advancedFormat plugin

// Manipulate
dayjs().add(7, 'day')
dayjs().subtract(1, 'month')

// Compare
dayjs('2024-01-15').isBefore('2024-06-01')
dayjs('2024-01-15').isAfter(dayjs())

// Display
dayjs().fromNow()  // needs relativeTime plugin
dayjs().toDate()

Step 4 - Enable plugins you need

import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import advancedFormat from 'dayjs/plugin/advancedFormat'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
import duration from 'dayjs/plugin/duration'

dayjs.extend(relativeTime)
dayjs.extend(advancedFormat)
dayjs.extend(customParseFormat)
dayjs.extend(utc)
dayjs.extend(timezone)

Step 5 - Timezone handling

moment-timezone
import moment from 'moment-timezone'

moment.tz('2024-01-15 12:00', 'America/New_York')
moment().tz('Europe/London').format()
dayjs timezone
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
dayjs.extend(utc)
dayjs.extend(timezone)

dayjs.tz('2024-01-15 12:00', 'America/New_York')
dayjs().tz('Europe/London').format()

Key differences to watch for

Option B - Migrate to date-fns

date-fns uses a functional API - every operation is a standalone function. Better for greenfield code than migration.

moment.js
import moment from 'moment'

// Format
moment().format('yyyy-MM-dd')

// Add days
moment().add(7, 'days').toDate()

// Check if before
moment('2024-01-15').isBefore('2024-06-01')

// From now
moment().fromNow()
date-fns
import { format, addDays, isBefore, formatDistanceToNow } from 'date-fns'

// Format
format(new Date(), 'yyyy-MM-dd')

// Add days
addDays(new Date(), 7)

// Check if before
isBefore(new Date('2024-01-15'), new Date('2024-06-01'))

// From now
formatDistanceToNow(new Date(), { addSuffix: true })

Check your CVE status first

If you are on moment.js 2.29.4 you have no unpatched CVEs. The migration is about future-proofing, not an emergency.

Check your current dependencies for CVEs before migrating.

Scan with PackageFix →

Free · No signup · No CLI · Runs in your browser

Common questions

Is moment.js broken?
Not broken - moment.js 2.29.4 works and has no unpatched CVEs. The issue is maintenance-only mode. The team will not add features or fix non-security bugs. Future CVEs may not get patches. For long-lived projects, migrating before you have to is easier than migrating under pressure.
Which replacement is easiest to migrate to?
dayjs has an almost identical API to moment.js. Most moment.js code works with dayjs after changing the import. date-fns uses a completely different functional API and requires rewriting your date logic.
Can I run moment.js and dayjs side by side?
Yes - both can be installed simultaneously. Migrate file by file, testing as you go. Remove moment.js when the last import is gone.
Does dayjs support all moment.js plugins?
dayjs has its own plugin ecosystem covering most moment.js plugin functionality. Check the dayjs plugin docs before migrating - duration, timezone, and relative time all have equivalents.
Is date-fns tree-shakeable?
Yes - this is one of date-fns's main advantages over moment.js. Only the functions you import get bundled. Typical date-fns usage adds 5-10KB vs moment's 67KB minimum.

Related