# yargs-parser turns a command line into a plain object, and its defaults surprise people

> yargs-parser is the option parsing layer that sits under yargs, taking a string or an array of arguments and returning a plain object with typed values, aliases and positional arguments. Its automatic transformations, camel-case expansion, dot-notation and number parsing, are the part to understand before you rely on them.

**yargs/yargs-parser** — :muscle: the mighty option parser used by yargs

- Repository: https://github.com/yargs/yargs-parser
- Website: http://yargs.js.org/
- Stars: 520 · Forks: 130
- Language: JavaScript
- License: ISC
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/yargs-yargs-parser

## One function, one plain object, no framework around it

yargs-parser is the option parser used by yargs, and it does one thing: it takes command line arguments and returns a plain mapping of keys to values.

The API is a single function. The parser expects args, which is a string or an array of strings representing the options to parse, and opts, a set of hints indicating how args should be parsed. It returns an object where the key and value pairs are each argument and their aliases, the `_` key holds an array of positional arguments, and there is an optional key holding arguments that came after the end-of-options flag.

The installation is a single npm command:

```sh
npm i yargs-parser --save
```

And the usage is two lines of JavaScript:

```js
const argv = require('yargs-parser')(process.argv.slice(2))
console.log(argv)
```

Run against a command line and you get a plain object:

```console
$ node example.js --foo=33 --bar hello
{ _: [], foo: 33, bar: 'hello' }
```

The string form is accepted too, so you can parse a command line you did not receive from the shell:

```js
const argv = require('yargs-parser')('--foo=99 --bar=33')
console.log(argv)
```

The point of the whole library is that the output is data, not a framework. There is no command object, no builder, no help generator, no validation layer. If you want those, you want yargs. If you want a good parser and nothing else, this is it.

## The type hints are how you stop numbers becoming strings

The opts object is where the interesting behaviour is configured, and one entry carries most of the weight: the type hints.

opts.string lists keys that should be treated as strings even if they resemble a number, so -x 33 with string: ['x'] does not become the number 33. opts.number lists keys that should be treated as numbers. opts.boolean lists keys that should be parsed as booleans, which matters for flags that take no argument and would otherwise swallow the next one.

opts.array indicates that keys should be parsed as an array, and it has a second form. You can indicate that keys should be parsed as an array and coerced to booleans or numbers, using entries like { key: 'foo', boolean: true } or { key: 'bar', number: true }.

opts.count indicates a key that should be used as a counter, so -vvv becomes {v: 3}. That is the standard verbosity flag idiom, handled by the parser rather than by hand.

opts.alias is an object representing the set of aliases for a key, in the form {alias: {foo: ['f']}}. The alias handling is one of the reasons the parser is more than a string splitter, because the alias map is inferred and returned separately by the detailed entry point.

opts.narg specifies that a key requires n arguments, in the form {narg: {x: 2}}. opts.default provides default values for keys. opts.coerce provides a custom synchronous function that returns a coerced value from the argument provided, or throws an error, and for arrays the function is called only once for the entire array. opts.normalize applies path.normalize() to values set to the key. opts.envPrefix selects which environment variables from process.env should be parsed, which is how a CLI picks up configuration from the environment without a separate config library. opts.config indicates a key that represents a path to a configuration file, which will be loaded and parsed, and opts.configObjects takes configuration objects to parse, with their properties set as arguments.

## Four transformations run by default, and each one changes your key names

The yargs-parser applies several automated transformations on the keys provided in args, and each one is on by default. This is the part that surprises people, because a flag name in your help text can end up under a different key in the parsed object.

Short option groups. A group of short options is treated as boolean flags, so -abc becomes { _: [], a: true, b: true, c: true }. With short-option-groups disabled, the same input becomes { _: [], abc: true }. The key is short-option-groups and the default is true.

Camel-case expansion. Hyphenated arguments are expanded into camel-case aliases, so --foo-bar yields { _: [], 'foo-bar': true, fooBar: true }. With camel-case-expansion disabled, you get { _: [], 'foo-bar': true } only.

Dot-notation. Keys that contain a dot are treated as objects, so --foo.bar gives { _: [], foo: { bar: true } }. Disabled, it is { _: [], "foo.bar": true }.

Number parsing. Keys that look like numbers are treated as such, so --foo=99.3 gives { _: [], foo: 99.3 }. Disabled, the value is the string "99.3". The same switch exists for positional numbers, under parse-positional-numbers, which is a separate key because a positional that looks numeric is a different decision from a flag that looks numeric.

All of these are turned on and off with the configuration field of opts, and the documented way to do it is:

```js
var parsed = parser(['--no-dice'], {
  configuration: {
    'boolean-negation': false
  }
})
```

The example switches off boolean-negation, which is the behaviour where --no-dice means dice is false.

The practical consequence is that a library which shells out to your CLI cannot assume a stable key name. If your CLI accepts --foo-bar and your consumer expects fooBar, that is the parser's camel-case expansion, and if you change the configuration later the consumer breaks silently.

## The detailed entry point is what yargs itself consumes

There is a second exported function, require('yargs-parser').detailed(args, opts={}), which parses a command line string and returns detailed information required by the yargs engine. The args and opts arguments are identical to the main function.

The return value has more than the parsed object. It gives argv with the key and value pairs, the `_` array of positionals and an optional array for arguments after the end-of-options flag. It gives error, populated with an error object if an exception occurred during parsing. It gives aliases, the inferred list of aliases built by combining lists in opts.alias. It gives newAliases, any new aliases added via camel-case expansion, with boolean set to true for a key like fooBar. It gives defaulted, any new argument created by opts.default with no aliases included, again flagged with a boolean. And it gives configuration, given by default settings and opts.configuration.

The separation of defaulted and newAliases from the main argv object is the interesting part. Those are the keys the parser invented rather than the keys the user typed, and yargs needs to know which is which so that it can distinguish a default from a user-supplied value. A library that wants the same behaviour needs this entry point rather than the plain one, because the plain one silently merges everything into one object.

It is also the only way to see the parse error as data. The main function throws; detailed returns the error in a field, which is what a framework that wants to render its own error message needs.

## The package ships three module formats, and the version 22 change is not cosmetic

The distribution story in this package is unusually broad, and it is driven by the fact that the same parser is expected to run in Node.js, in a browser and in Deno.

The package.json declares type as module, with the main entry at build/lib/index.js and an exports map that has an import condition and a fallback require condition, plus a separate ./browser subpath pointing at browser.js. The browser build is a separate file at the repository root rather than something the build produces, which is why the exports map has a second entry for it.

The ESM example is the modern form:

```js
import parser from 'yargs-parser'

const argv = parser('--foo=99 --bar=9987930', {
  string: ['bar']
})
console.log(argv)
```

The browser example loads the same thing from a CDN as an ES module inside a script tag, importing from the unpkg browser.js build. The README notes that ESM support, both in Node.js and in the browser, is available as of v19.

Deno support is also as of v19, and it is imported from a URL rather than from a registry:

```typescript
import parser from "https://deno.land/x/yargs_parser/deno.ts";

const argv = parser('--foo=99 --bar=9987930', {
  string: ['bar']
})
console.log(argv)
```

So there is a deno.ts entry at the repository root alongside browser.js, each providing the same API for a runtime that cannot use the npm package.

The version history is worth noting for anyone pinning. The recent releases are v22.0.0 on 2025-05-26, v21.1.1 on 2022-08-04 and v21.1.0 on 2022-08-03. The gap between 21.1.1 and 22.0.0 is nearly three years, and the 22.0.0 release is where the package type changed to module, which is a breaking change for CommonJS consumers. The repository uses release-please with a manifest and a config file for its release process, and Renovate is configured for dependency updates, so the version line is managed automatically rather than by hand.

## Where yargs-parser is the wrong tool

The first wrong-tool case is a CLI that needs commands, help or validation. yargs-parser parses options; it does not know what a command is, does not generate a help screen, and does not check that a required option is present. The documentation for yargs itself is where the usage instructions live, and the yargs website is where the thorough examples are.

The second wrong-tool case is a codebase where the default transformations would fight the design. If your flags already use dots for a different purpose, or if you have a flag literally named --no-cache meaning something other than negating cache, the default configuration is wrong for you and you have to turn features off one by one, in every place the parser is invoked.

The third wrong-tool case is a CommonJS project that cannot take a major version bump, because v22 is ESM only. A project on Node 18 and below, or one whose build tooling is still CommonJS throughout, is stuck on the 21.x line with no forward movement.

For the alternative approach, the difference in approach is between using the parser and writing the parsing. A hand-written parser over process.argv is not hard for a handful of flags, and it gives you exact control over what --foo-bar means and whether 33 is a number or a string. The difference in approach is that yargs-parser gives you a large set of behaviours for free, and you pay for them in surprises and configuration. A hand-written parser gives you zero surprises and you pay for it in code.

The other alternative is a different library with a different set of defaults, where the argument is that not every parser needs to be yargs-compatible, and choosing one whose defaults match your conventions removes the configuration work entirely.

And the third option is to write no parser at all, if your CLI takes a single positional argument or reads a config file, because a dependency that parses arguments is not needed for a tool that takes one.

## ISC licensed, conventionally tested, and openly maintained

The licence is ISC, and the repository carries a LICENSE.txt file.

The maintenance posture is what you would expect from a package that a large number of command line tools depend on indirectly. The last push to the repository was on 2026-10-11, and the CI badge points at a GitHub Actions workflow named ci. The changelog is a CHANGELOG.md, the contribution guide is CONTRIBUTING.md, and there is a SECURITY.md for reporting vulnerabilities, plus an AI_POLICY.md that sets out the project's stance on AI-generated contributions.

The tooling is modern and conventional for a TypeScript project written in JavaScript with type checking. The package has a tsconfig.json and a tsconfig.test.json, and the compile script is a plain tsc. The test script is c8 with the text and html reporters running mocha against test/*.mjs, with a pretest step that clears the build directory and compiles the test configuration first. There is a separate browser test script that serves the directory on port 8080 and runs the browser test file against it, and a separate TypeScript test script that runs the compiled TypeScript tests. Coverage has its own script that runs c8 report with a check-coverage flag, and the .nycrc configuration is in the repository root.

Lint and format are handled by gts, with a check script and a fix script, and the ESLint and Prettier configurations are committed as .eslintrc.json and .prettierrc.cjs, with .eslintignore and .prettierignore alongside them.

The docs/ directory holds the documentation, and the top-level entry is README.md. The package keywords are argument, parser, yargs, command, cli, parsing, option and args, and the author field credits Ben Coe.

## Conclusion

yargs-parser suits a JavaScript project that wants yargs-grade option parsing without the command DSL, or that wants to reuse yargs's parsing rules inside a framework of its own. It does not suit a project that wants a command hierarchy, help output or validation, because yargs itself provides those and yargs-parser does not. Before adopting, read the configuration section and turn off the transformations you do not want, since short-option groups, camel-case expansion, dot-notation and number parsing are all on by default, and pin the major version because v22 changed the package type to ESM.

## FAQ

### What does yargs-parser return when it parses arguments?

An object with a key and value pair for each argument and its aliases, an underscore key holding an array of positional arguments, and an optional double-underscore key holding arguments that came after the end-of-options flag.

### How do I stop yargs-parser from turning numbers into numbers?

Pass a configuration with the parse-numbers key set to false, or mark the specific key in opts.string so that keys should be treated as strings even if they resemble a number. The same pattern applies to parse-positional-numbers for positional values.

### What is the difference between yargs-parser and yargs?

yargs-parser only parses options into a plain object, while yargs is the full command line framework with commands, help output and validation built on top of it. The detailed() function returns the extra information the yargs engine needs, such as inferred aliases and parse errors.

### Does yargs-parser work outside Node.js?

Yes. The package ships a browser.js build and a deno.ts entry point, and the README documents ESM usage in both Node.js and the browser, plus a Deno example importing from deno.land. ESM and Deno support are both described as available as of v19.

## Sources

- [License: ISC](https://github.com/yargs/yargs-parser/blob/main/LICENSE)
- [Project website](http://yargs.js.org/)
- [README](https://github.com/yargs/yargs-parser/blob/main/README.md)
- [Releases](https://github.com/yargs/yargs-parser/releases)
- [yargs/yargs-parser on GitHub](https://github.com/yargs/yargs-parser)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/yargs-yargs-parser
