Open-source project
moment/moment avatar
moment/moment

Moment.js in maintenance mode: what still ships, and what the build scripts refuse

GitHub describes it as Parse, validate, manipulate, and display dates in javascript.. The repository metadata lists JavaScript as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

47,902 stars6,980 forksJavaScriptMIT

At a glance

What is it?
Moment.js still publishes releases, but the project has declared itself a legacy library that will not accept new features. The useful details for a reader today are in its two entry points, its split TypeScript declarations, and the four-step lint chain that gates a release.
Who is it for?
Choose Moment.js when an existing codebase already depends on it and you want an upgrade path that does not rewrite every call, not when you are choosing a date layer for a new project. Do not plan a feature on it, because new features are not being accepted.
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 15 days ago.
What is it written in?
Mainly JavaScript, 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

Maintenance mode is a README notice, and the release list still moved twice in 2026

The opening notice calls Moment.js a legacy project in maintenance mode, says new features are not being accepted, and points at the latest project status update on momentjs.com/news. That one paragraph is the most consequential thing on the page, and the rest of the repository is arranged around it. The repository is not archived, the last push landed on 2026-09-15, and the release list shows 2.31.0 on 2026-09-15 and 2.30.1 on 2026-07-26.

For a reader the split is practical rather than rhetorical. Installing and upgrading keep working, because tagged versions keep appearing, and no new capability will come out of this project. If your roadmap needs a formatting token, a locale or a date API that Moment.js does not have today, a pull request here will not deliver it. Read the status update page before you plan around that, and treat any Moment.js feature request you find in an issue tracker as belonging to a fork or a different library.

main and jsnext:main point at two different builds of the same library

The package declares two entry points: `main` is `./moment.js` and `jsnext:main` is `./dist/moment.js`. Both names appear among the top-level entries, with `moment.js` sitting next to `package.json` and a `dist/` directory sitting beside it, so these are two distinct files in the repository rather than one file with a fallback.

The consequence is that which artifact your application loads is decided by your bundler, not by you. A tool that reads `main` gets the file at the repository root, and a tool that reads `jsnext:main` gets the built file under `dist/`. Neither the README nor the documentation link it carries explains how those two artifacts differ, so a bundle-size comparison between them will not tell you what your app is shipping until you inspect the toolchain. The `build` script that produces the second one is `node scripts/build.js`.

TypeScript declarations fork at version 3.1 through typesVersions

Type support here is not one file. `typings` points at `./moment.d.ts`, which sits at the top level, and a `typesVersions` block redirects every current TypeScript consumer: for `>=3.1` the wildcard `*` resolves to `ts3.1-typings/*` instead. That directory is in the tree, and so is a `typing-tests/` directory next to it.

The consequence is that on a modern TypeScript version the declarations your editor loads are not the file named by `typings`. Any deep import you add has to resolve inside `ts3.1-typings/` for every consumer on 3.1 or later, and the `>=3.1` block also pins one specific path, `min/moment-with-locales`, to `ts3.1-typings/moment.d.ts`. The presence of `typing-tests/` at the top level is what tells you the project intends those paths to be exercised rather than assumed, so a contribution that only updates `moment.d.ts` leaves the branch most people compile against untouched.

Two build scripts, and only one of them pulls in the locales

The two build entry points differ by a single flag. `build` is `node scripts/build.js`, and `build:custom` is `node scripts/build.js --locales`, so the locale set is opt-in at build time. The published typings still reference a combined file, `min/moment-with-locales`, and a `min/` directory sits in the tree beside `dist/` and `locale/`.

The consequence for a reader is a distribution choice with a cost attached to each side. A build without `--locales` produces the core and leaves you importing individual locale files, which keeps the payload small. A build that includes locales produces a combined artifact carrying all of them, and that artifact has its own typing path in the package, wired separately from the core declarations. The `release` script ends with a `release:minify` step, so the minified output is produced during publishing rather than as a manual side task.

engines accepts any Node, while devEngines pins three majors with onFail error

The same package.json is permissive and strict at once. `engines` declares `node` as `*`, which is why `npm install moment` installs in whatever environment a consumer happens to have. `devEngines` then requires the runtime to be `^22.22.2 || ^24.15.0 || >=26.0.0` with `onFail` set to `error`, and `packageManager` is pinned to `[email protected]`. The lockfile in the tree is `pnpm-lock.yaml`, and `pnpm:devPreinstall` runs `node scripts/install-hooks.js` to install the git hooks.

The consumer side is one line, and it is the only command the README gives:

bash
npm install moment

The consequence is a sharp split between consuming the library and working on it. As a dependency it has no Node floor. As a repository it refuses to run its own scripts on the version of Node you happen to have, fails the install instead of warning, and expects pnpm plus installed hooks from a `.githooks/` directory. A patch you intend to send upstream has to be prepared in that environment rather than in the one your application runs in.

npm run lint is four separate checks, and one of them parses month names

The lint script is not one pass. `lint` expands to `run-s lint:eslint lint:markdown lint:months-parse format:check`, which means these four steps, in this order:

bash
eslint scripts src
markdownlint-cli2 "**/*.md"
node scripts/check-months-parse.js
prettier --check .

The middle entry is the one worth reading twice: a script whose name says it parses month names is a gate that runs on every change, and it reads locale data rather than a test file.

The consequence for a contributor is that a change to a locale can fail this chain without touching JavaScript at all, and a change confined to documentation can fail on markdownlint or on prettier formatting. The script that fixes the last of those is `prettier --write .`, and `.prettierrc`, `.prettierignore` and `.markdownlint-cli2.jsonc` at the top level are the configuration those tools read. `eslint.config.js` and `.editorconfig` cover the code side, and `run-s` means the four steps stop at the first failure.

The README ships no API example, only a pointer to momentjs.com/docs

Read past the installation section looking for a usage example and there is none. The README is a short list of pointers: documentation on momentjs.com, a Changelog link to `CHANGELOG.md`, a Stack Overflow tag, a Security Policy link to `SECURITY.md`, a Threat Model link to `THREAT_MODEL.md`, and one install command. The description line at the top is the whole statement of what the library does, parsing, validating, manipulating and formatting dates.

The consequence is that a reader who clones the repository to learn the API has to leave the repository. Format tokens, locale names, parsing rules and deprecation warnings all live on the hosted documentation, and CHANGELOG.md is the only in-repository record of how behaviour changed between versions. For a library people depend on for years, that puts the authoritative behaviour description somewhere other than beside the code, and the version in your lockfile is what ties the two together.

MIT covers the code, and the OpenJS Foundation trademark list covers the name

Licensing here is two separate things. The code is under the MIT license, described as freely distributable, with copyright held by the OpenJS Foundation and Moment.js contributors. Alongside that, the page states that the OpenJS Foundation has registered trademarks, links a Trademark Policy and a Trademark List, and says marks not on that list belong to their respective holders and that using them implies no affiliation or endorsement. An AI Coding Assistants Policy, Bylaws, a Code of Conduct, Terms of Use and a Cookie Policy sit under the same foundation.

The consequence for a reader is that the MIT grant settles redistribution of the code and says nothing about the name. Anyone shipping Moment.js inside a product can rely on the license, and anyone republishing under a name that collides with the foundation's marks has a separate question to answer before shipping. The same separation shows up in the repository itself, where SECURITY.md, THREAT_MODEL.md, RELEASING.md, CONTRIBUTING.md and AGENTS.md are maintained as files next to an `.agents/` directory rather than left as unwritten knowledge.

Editorial conclusion

Choose Moment.js when an existing codebase already depends on it and you want an upgrade path that does not rewrite every call, not when you are choosing a date layer for a new project. Do not plan a feature on it, because new features are not being accepted. Before committing, read the 2.31.0 entry in CHANGELOG.md for parsing changes that affect your inputs, and remember that working on the repository itself needs pnpm, one of three pinned Node majors, and installed git hooks.

Frequently asked questions

How do I install Moment.js?

The installation command is `npm install moment`, and the same section says to use a package manager like npm, yarn or bun. The package declares `engines.node` as `*`, so there is no Node version floor for consumers.

Does Moment.js install differently with other package managers?

The README points at npm, yarn and bun as the supported package managers and shows only the npm command. For working on the repository itself, package.json pins `packageManager` to `[email protected]` and the tree carries `pnpm-lock.yaml`.

What is Moment.js?

A JavaScript date library for parsing, validating, manipulating and formatting dates, MIT licensed and an OpenJS Foundation project. Its own README calls it a legacy project in maintenance mode, where new features are not being accepted, and the latest version listed is 2.31.0.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
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/moment-moment.svg)](https://hysenlabs.com/projects/moment-moment)