Commander vs yargs in 2026: CLI Argument Parsing
·PkgPulse Team
TL;DR
Commander for simple CLIs; yargs for complex CLIs with subcommands, completions, and built-in validation. Commander (~50M weekly downloads) is minimal, zero-dependency, and used in Vue CLI, Create React App, and many popular tools. yargs (~30M downloads) is heavier but auto-generates help, handles validation, and has better subcommand support. For most CLIs, Commander's simplicity wins.
Key Takeaways
- Commander: ~50M weekly downloads — yargs: ~30M (npm, March 2026)
- Commander is zero-dependency — yargs has 3 dependencies
- yargs generates help automatically — more detailed
--helpoutput - Both support subcommands — yargs has better subcommand file loading
- Commander is TypeScript-first — v8+ has excellent type definitions
Basic CLI
// Commander — simple, clean API
const { Command } = require('commander');
const program = new Command();
program
.name('my-tool')
.description('A useful CLI tool')
.version('1.0.0');
program
.command('deploy')
.description('Deploy to production')
.argument('<environment>', 'Target environment (staging|production)')
.option('-f, --force', 'Force deploy without confirmation')
.option('-t, --timeout <seconds>', 'Deployment timeout', '60')
.action((environment, options) => {
console.log(`Deploying to ${environment}...`);
if (options.force) console.log('Forcing deployment');
console.log(`Timeout: ${options.timeout}s`);
});
program.parse();
// yargs — similar but more config
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
yargs(hideBin(process.argv))
.scriptName('my-tool')
.command(
'deploy <environment>',
'Deploy to production',
(yargs) => {
yargs
.positional('environment', {
describe: 'Target environment',
choices: ['staging', 'production'], // Auto-validated
})
.option('force', {
alias: 'f',
type: 'boolean',
description: 'Force deploy without confirmation',
})
.option('timeout', {
alias: 't',
type: 'number',
default: 60,
description: 'Deployment timeout in seconds',
});
},
(argv) => {
console.log(`Deploying to ${argv.environment}...`);
}
)
.demandCommand(1, 'Please specify a command')
.help()
.argv;
TypeScript Support
// Commander v8+ — full TypeScript types
import { Command } from 'commander';
interface DeployOptions {
force: boolean;
timeout: string;
}
const program = new Command();
program
.command('deploy')
.argument('<environment>', 'Target environment')
.option('-f, --force', 'Force deploy', false)
.option('-t, --timeout <secs>', 'Timeout', '60')
.action((environment: string, options: DeployOptions) => {
// Both typed correctly
});
// yargs — TypeScript with inference
import yargs from 'yargs/yargs';
interface DeployArgs {
environment: 'staging' | 'production';
force: boolean;
timeout: number;
}
yargs(hideBin(process.argv))
.command<DeployArgs>(
'deploy <environment>',
'Deploy to production',
(yargs) => yargs
.positional('environment', { type: 'string', choices: ['staging', 'production'] as const })
.option('force', { type: 'boolean', default: false })
.option('timeout', { type: 'number', default: 60 }),
(argv) => {
// argv is DeployArgs — typed
argv.environment; // 'staging' | 'production'
argv.timeout; // number
}
)
.argv;
Auto-generated Help
# Commander help output:
$ my-tool deploy --help
Usage: my-tool deploy [options] <environment>
Deploy to production
Arguments:
environment Target environment (staging|production)
Options:
-f, --force Force deploy without confirmation
-t, --timeout <seconds> Deployment timeout (default: "60")
-h, --help display help for command
# yargs help output (more detailed):
$ my-tool deploy --help
my-tool deploy <environment>
Deploy to production
Positionals:
environment Target environment [string] [required] [choices: "staging", "production"]
Options:
--force, -f Force deploy without confirmation [boolean] [default: false]
--timeout, -t Deployment timeout in seconds [number] [default: 60]
--help, -h Show help [boolean]
--version, -v Show version number [boolean]
yargs generates more structured help with types and defaults shown.
When to Choose
Choose Commander when:
- Simple CLI with straightforward commands
- Zero-dependency is important (bundled CLIs, npm packages)
- You prefer minimal API surface
- TypeScript project (Commander v8+ has excellent types)
- Most popular CLIs in the JS ecosystem use Commander
Choose yargs when:
- Complex CLI with many subcommands and options
- Built-in validation (choices, required args)
- Shell completion generation
- More detailed auto-generated help matters
- You need middleware/before hooks on commands
Compare Commander and yargs package health on PkgPulse.
See the live comparison
View commander vs. yargs on PkgPulse →