![notion image](https://www.notion.so/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2F3857fc26-f9e3-4f87-bcbc-e6999eee1065%2Fstn-dh98RS974HMeAWDVKz9TZuinW5a26iZ8CmYPXulA.jpeg?table=block&id=49bb4d35-1432-4a9e-bb77-0a5b6b0f4298&cache=v2)
Node.js is a very popular choice for developers writing CLI applications. The platform’s rich developer ecosystem has a large number of mature packages to help create elegant, interactive command line tools.
One challenge for developers is understanding the landscape and selecting the right set of packages. There are tons of libraries to choose from but many are rather dated. Some have stalled in development, some have been abandoned by their maintainers, and many have not caught up with modern development practices. This post is meant to categorize the most popular packages and highlight a few of them and their current development state.
To quickly try out any of the mentioned packages, you can use the following tool:
$ npm install --global try-node-cli-packages $ try commander $ try meow $ try chalk
The repo for this tool is available here: joeykilpatrick/try-node-cli-packages.
🗒 A Note
Modern JavaScript/TypeScript development has evolved dramatically over the last decade. TypeScript’s ever-growing popularity and the introduction of new Node.js and ECMAScript features has changed common programming idioms. Unlike modern web development, modern CLI development doesn’t have new frameworks and libraries popping up every week. Instead, a much more mature ecosystem has emerged, but it is slow to adapt to changes in the language.
Remember that many of the most popular libraries listed here use programming styles that are slightly dated. Most major ones were designed before ES6 classes were released in 2015 and while all libraries listed here have either added type declarations (.d.ts files) to the package or have types available through DefinitelyTyped, virtually none were designed with strong type inference in mind. Today, no major library has been rewritten in TypeScript.
Also, be cautious not to give too much weight to the number of weekly downloads for each package. These metrics should not be relied upon as an indicator of popularity with developers. Inclusion in a major package can dramatically skew the download numbers. For example, this week there were 22 million downloads of
arg
. However, the popular ts-node
package includes arg
as a dependency and it has been downloaded 19 million times this week.Many packages here have trade-offs. Some prioritize speed, some prioritize a small package size, and some prioritize developer experience. Only you can choose which library best fits your needs.
🤝 Major Contributors
While there are hundreds of contributors to these libraries, there are some names that repeatedly come up as creators, maintainers, and contributors to packages in this space. Consider sponsoring their open-source work on GitHub. I am not affiliated in any way. Packages with large contributions from these users are marked with the corresponding emoji in the lists below.
sindresorhus 🦄
Sindre Sorhus is responsible for many of the most widely used packages for output styling in the terminal. Besides well-known packages like
chalk
and meow
, he has authored many packages that are used internally by other major packages such as cli-cursor
(used by inquirer
, ora
, and ink
) and supports-color
(used by mocha
, nodemon
, and serverless
).lukeed 🐒
Luke Edwards is the author of multiple lightweight, performance-focused CLI libraries. His package
kleur
is a lightweight alternative to chalk
and is used primarily by prompts
. His package mri
is a high-speed alternative to minimist
.Qix- 🦀
Qix is the creator of
arg
and is a frequent collaborator of Sindre Sorhus with both users co-authoring packages in the chalk GitHub Org.Argument Parsing
These libraries form the base of most CLI apps. Some of these packages style themselves as “argument parsers”, some as “CLI helpers”, but at their core they all help developers declare which arguments, commands, and flags that the app expects and automatically parse the arguments provided via
process.argv
. Typically, a developer should only need one of these.Package | Version | Downloads | Last Publish | First Publish | Types |
10.0.0 | 109 M | 3 days ago | August 2011 | Includes .d.ts | |
21.1.1 | 79 M | 6 months ago | January 2016 | DT Types | |
17.6.2 | 77 M | 2 months ago | November 2013 | DT Types | |
1.2.7 | 53 M | 3 months ago | June 2013 | DT Types | |
7.0.0 | 25 M | 3 months ago | March 2011 | DT Types | |
arg🦀 | 5.0.2 | 22 M | 7 months ago | October 2017* | Includes .d.ts |
meow🦄 | 11.0.0 | 18 M | 3 months ago | October 2014 | Includes .d.ts |
mri🐒 | 1.2.0 | 7 M | 1 year ago | April 2017 | Includes .d.ts |
sade🐒 | 1.8.1 | 1.9 M | 1 year ago | May 2017 | Includes .d.ts |
📦 commander, sade, yargs
These are opinionated packages that all use a “fluent” interface of chained methods to describe the CLI app. They all come with automatically generated help text and other useful features out-of-the-box. Because of the “fluent” interface, the possibility for strong type inference for flags and arguments is limited.
The oldest package in this list is
commander
which is used by CLI apps like webpack-cli
and babel-cli
. This no-dependency library is very large and feature-rich with lots of documentation. Development is still active on the project with new features released often and a very high response rate to issues and PRs on GitHub.A faster, lightweight alternative to
commander
is sade
. With a package size of 31.5 KB as opposed to commander
’s 174 KB, it offers all the same core functionality with an almost identical interface. Development is not active on the project, but it still serves well as a simpler replacement for commander
.The other comparable package is
yargs
, used by CLI apps such as mocha
and nyc
. One of its advantages is support for messages in a variety of different locales. While bigger than commander
at 290 KB, it doesn’t provide a lot more functionality. The syntax also relies more heavily on nested callback-style arrow functions which may be less readable for some developers.📦 meow
As an alternative to the opinionated ones above,
meow
is a smaller, unopinionated package. It uses a declarative style interface to define the expected flags instead of the “fluent” one used in the packages above. Due to this declarative style, type inference is much stronger than all others in this list. It comes with a few useful features like automatic version and help flags and provides lots of flexibility for the developer. It is also one of the first to only be available as an ES Module. Its repo is active but new releases are mostly limited to maintenance and bug-fixes.📦 minimist, yargs-parser, mri, nopt, arg
For developers who are looking for the maximum amount of control, these packages are bare-bones argument parsers. Their scope is only to parse the provided flags and arguments. While all of them include a feature for flag aliases, these packages typically don’t even include features like required flags, unexpected flags, default flag values, or help messages. Type inference for parsed flags is virtually non-existent, with most typed as
any
. Most of these packages are no longer actively developed and are considered completed.The
minimist
package was the argument parser for the now-defunct optimist
package.The
yargs-parser
package is the underlying argument parser for yargs
and meow
and styles itself as the successor of minimist
. At 128 KB, it is substantially larger than every other parser in this section and is feature-rich (some might say bloated). It is used directly by popular CLI apps like mocha
and ts-jest
.The
mri
package is an ultra-fast, lightweight alternative to minimist
and yargs-parser
with an identical interface. It is about 10% of the size of yargs-parser
. It is also the underlying parser for the package sade
.An alternative to the interface in the previous three packages can be found in the
nopt
package which was developed by npm for the npm
package. It is feature-rich with multiple additional types of flag values like “path” or “stream” values. It also supports flag aliases a bit differently than the others listed here which may help some developers write more expressive flag aliases.The
arg
package has a similar interface to nopt
but a bit more stripped down. It is used by ts-node
.Output Styling
These libraries allow developers to manipulate how terminal output is displayed. They add string formatting, colors, or small text-based animations.
Package | Version | Downloads | Last Publish | First Publish | Types |
6.2.1 | 232 M | 3 months ago | July 2013 | Includes .d.ts | |
chalk🦄🦀 | 5.2.0 | 189 M | 1 month ago | August 2013 | Includes .d.ts |
8.0.1 | 70 M | 1 year ago | August 2015 | DT Types | |
6.0.0 | 31 M | 4 months ago | August 2015 | Includes .d.ts | |
4.1.5 | 17 M | 7 months ago | November 2018 | Includes .d.ts | |
ora🦄 | 6.1.2 | 16 M | 7 months ago | March 2016 | Includes .d.ts |
7.0.1 | 12 M | 1 month ago | December 2015 | Includes .d.ts | |
0.14.3 | 2 M | 4 years ago | June 2016 | DT Types |
📦 chalk, kleur, ansi-styles, …
There are many, many packages dedicated to providing colors and formatting for terminal text. The process includes inserting ANSI escape sequences into the text to tell the terminal how the text should be formatted. There are lots of different terminal implementations and lots of edge cases to consider.
The most well known of these packages is
chalk
which is one of the most depended-on packages on npm with over 90,000 dependent packages. It is easy to use, extremely well maintained, and extremely reliable.Over the years, similar packages to
chalk
have been developed, forked, and rebranded, leading to lots of different packages that all do nearly the same thing. Competitors to chalk
include colors
, picocolors
, ansi-colors
, colorette
, kleur
, and nanocolors
. The history of some of these other packages is wild.Of the packages listed here, only
colors
predates chalk
. It was famously sabotaged by its owner in January 2022 causing widespread issues for numerous tools (article, article, article, owner’s manifesto). The package was reverted by npm and the author was suspended from GitHub. Since he was the only maintainer of the colors
package, it is de-facto abandoned. In the aftermath, many dependent packages switched to chalk
.While not as exciting as the
colors
disaster, there has been plenty of drama in the open-source community around other chalk
alternatives. Consider this August 2018 clash between the maintainers of ansi-colors
and colorette
, or this September 2021 train wreck between the maintainers of colorette
and nanocolors
(with input from the maintainers of chalk
).Those who have attempted to improve on
chalk
have tried to create alternatives that are faster or smaller or that have fewer dependencies. In these attempts, authors have removed features, missed edge cases, and sometimes introduced bugs and memory leaks. Multiple authors have published metrics using their own benchmarks for comparing their creations to chalk
. But a true apples-to-apples comparison between these packages is impossible if they don’t cover the same edge cases and include the same set of features. Meanwhile, chalk
has continued to improve and is now much faster (as of v3), much smaller (as of v5), and dependency-free (as of v5).For the vast majority of packages both large and small,
chalk
should be used. For those who need a smaller package size with fewer features, consider kleur
which is well maintained and non-controversial. For a much smaller package size with only the absolute basics, consider the new yoctocolors
package from the same author as chalk
at a tiny 7.6 KB.For packages that need very fine-grained controls for inserting ANSI escape codes,
ansi-styles
is from the same maintainers as chalk
and has a lower-level interface that allows developers to insert individual ANSI codes into their text.📦 ora
The
ora
package provides controllable spinner animations for the terminal. A large number of spinner animations are included through the cli-spinners
package, but custom animations are possible as well. The provided interface works great for common use cases such as displaying an action in progress that may succeed or fail.📦 boxen, wrap-ansi
These packages help format blocks of terminal text. To wrap text to a specific number of columns, use
wrap-ansi
. To draw a box around your text, use boxen
. It comes built-in with lots of different types of borders.User Input
Node.js provides user keyboard input through
process.stdin
and readline
, but using either of these directly is challenging. Multiple packages are available that output a prompt to the console and wait for user input. Some single-purpose libraries are available for some common use cases such as password-prompt for displaying typed input as ‘***’ and yesno or prompt-confirm for user confirmation. The following libraries are general-purpose user input libraries. Typically, a developer should only need one of these.Package | Version | Downloads | Last Publish | First Publish | Types |
9.1.4 | 26 M | 3 months ago | May 2013 | DT Types | |
2.4.2 | 20 M | 1 year ago | February 2018 | DT Types | |
2.3.6 | 13 M | 3 years ago | August 2016 | Includes .d.ts | |
3.2.0 | 1 M | 2 years ago | January 2013 | DT Types | |
1.4.10 | 900 K | 4 years ago | August 2013 | DT Types | |
1.3.0 | 600 K | 9 months ago | March 2011 | DT Types |
📦 inquirer, prompts, enquirer
These three packages have similar interfaces and have built-in support for common prompt types. They use some slightly different terminology in their interfaces (e.g.
default
vs. initial
, or title
vs. name
) and sometimes behave differently in unexpected ways. For instance, when a user presses the CTRL+C sequence, inquirer
immediately kills the process, enquirer
throws an error, and prompts
cancels the prompt but continues execution without error. Additionally, all three have some demonstrably wrong types that make development with TypeScript difficult.The
inquirer
package is the oldest of the three and has the largest market-share by downloads and number of dependent packages. It is strengthened by its ability to register custom prompt types via other 3rd party packages such as inquirer-autocomplete-prompt
, inquirer-directory
, and inquirer-checkbox-plus-prompt
. It is still under active development by its author and has been undergoing a full refactor since 2018.The
prompts
package has the most accurate type declarations and a full TypeScript rewrite is planned, though development has stalled. Currently, there is no easy way to extend and customize prompt types. For tests, the package allows developers to “inject” answers to the prompts, bypassing stdin
.The
enquirer
package was originally meant to be a smaller, faster, better alternative to inquirer
, but today enquirer
is twice the size of inquirer
. It added many additional types of useful, elegant prompts like scales, sorts, and snippets. It also introduced a new way to extend and customize existing prompt types using ES6 classes. However, type declarations were never added for this new feature and the package has not seen a new release in over 3 years.📦 promptly, readline-sync, prompt
These are less-mature packages than the ones above and don’t offer nearly any additional functionality. They have less elegant user interfaces and do not offer built-in features for common kinds of prompts such as numbers, lists, or selections. Also, while
promptly
is substantially smaller than the ones above at only 15.5 KB, the other two are about the same size as prompts
and enquirer
. They are not actively developed or maintained and should probably not be used in new projects.Frameworks
All the packages above are designed to handle one particular aspect of CLI development. However, there are a few other packages that don’t fit into any of the above categories and aim to provide developers with an entire suite of tools to build their CLI app.
Package | Version | Downloads | Last Publish | First Publish | Types |
2.0.8 | 794 K | 5 hours ago | February 2018* | Native | |
ink🦄 | 3.2.0 | 283 K | 1 year ago | July 2017* | Native |
1.12.0 | 39 K | 6 years ago | August 2015 | DT Types |
📦 oclif
In 2018, Salesforce announced that they were open-sourcing their
oclif
framework that they had built to develop some of their CLI apps (e.g. the Heroku CLI). It is a large, sprawling framework with plugins, auto-generated scaffolding, built-in testing tools, and more. It is heavily opinionated and is reminiscent of large web frameworks like Angular or React.For giant CLI apps with lots of
git
-style subcommands, oclif
is a standout choice. For smaller CLI apps, it may be overkill.📦 ink
A fundamentally different paradigm than all other packages listed in this post,
ink
allows developers to define React components that are then rendered for the terminal. Instead of having to format and print individual strings of text output, ink
allows users to define reactive XML components that are repeatedly re-drawn in the user’s terminal. The expressive interface lets developers define multi-line terminal animations that would otherwise be extremely complex and brittle.Out of all the packages in this list, I find this one to be the most astonishing. The concept and the execution of this package are wildly impressive. This package redefines what is possible with terminal output and a CLI user’s experience.
📦 vorpal
Most CLI apps follow the same execution model: a user runs a terminal command which spawns a process that then parses the provided arguments and options, executes some action, and then closes. The
vorpal
package takes a very different approach, styling its method as “immersive”. When a vorpal
application is run, its process opens a new sub-terminal that allows users to input further application-specific commands. This kind of application can be viewed not as a set of isolated commands, but an entire implementation of a shell. An entirely new set of possibilities emerge with this new paradigm. Variables set by previous commands can be accessed by subsequent calls. Applications can support autocompleting commands. Users can pipe commands together. The code for defining sub-commands uses the same style of “fluent” interface of commander
and others, which comes with the same set of drawbacks.While this approach is novel, it has failed to see significant usage and development. The
vorpal
project was abandoned in 2018. An effort to fork and “reforge” it was also abandoned in 2019. The cliffy
package appears to be inspired by vorpal
and is under somewhat active development, but is even less mature and has seen little adoption.Today,
vorpal
is more of an inspiration for a future “immersive” CLI package rather than a viable option for new applications.