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.
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.
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.
Sindre Sorhus is responsible for many of the most widely used packages for output styling in the terminal. Besides well-known packages like
meow, he has authored many packages that are used internally by other major packages such as
cli-cursor (used by
supports-color (used by
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
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.
|3 days ago
|6 months ago
|2 months ago
|3 months ago
|3 months ago
|7 months ago
|3 months ago
|1 year ago
|1 year ago
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
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
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
The other comparable package is
yargs, used by CLI apps such as
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.
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.
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.
minimist package was the argument parser for the now-defunct
yargs-parser package is the underlying argument parser for
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
mri package is an ultra-fast, lightweight alternative to
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
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.
arg package has a similar interface to
nopt but a bit more stripped down. It is used by
These libraries allow developers to manipulate how terminal output is displayed. They add string formatting, colors, or small text-based animations.
|3 months ago
|1 month ago
|1 year ago
|4 months ago
|7 months ago
|7 months ago
|1 month ago
|4 years ago
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
nanocolors. The history of some of these other packages is wild.
Of the packages listed here, only
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
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
colorette, or this September 2021 train wreck between the maintainers of
nanocolors (with input from the maintainers of
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 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.
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.
Node.js provides user keyboard input through
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.
|3 months ago
|1 year ago
|3 years ago
|2 years ago
|4 years ago
|9 months ago
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.
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.
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-checkbox-plus-prompt. It is still under active development by its author and has been undergoing a full refactor since 2018.
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
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.
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
enquirer. They are not actively developed or maintained and should probably not be used in new projects.
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.
|5 hours ago
|1 year ago
|6 years ago
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
oclif is a standout choice. For smaller CLI apps, it may be overkill.
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.
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.
vorpal is more of an inspiration for a future “immersive” CLI package rather than a viable option for new applications.