Skip to content
JavaScript Basics: Add Command-Line Options with parseArgs

JavaScript Basics: Add Command-Line Options with parseArgs

October 14, 2026

A command-line option lets someone change a script’s behavior without editing its source. This tutorial builds a small greeting command with a name, repetition count, uppercase switch, and help message. Node’s built-in parseArgs handles the option syntax; our code checks what the values mean.

Prepare a scratch directory

You need Linux, Bash, a text editor, and Node.js on your PATH. The commands were tested with Node.js v24.19.0. They require no npm packages, package manifest, network access, or application environment variables. The .mjs extension makes the source a JavaScript module.

Run these commands and keep this Bash session open:

node --version
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

The tested version prints:

v24.19.0

Use your editor to save the following UTF-8 source file inside the temporary directory. This script only prints messages; it does not modify files or contact a service.

Describe the accepted options

Save as greet-with-options.mjs:

import { parseArgs } from "node:util";

const usage = `Usage: node greet-with-options.mjs [options]
  -n, --name TEXT   Name to greet (default: reader)
  -t, --times N     Repeat 1 to 5 times (default: 1)
  -u, --upper       Use uppercase
  -h, --help        Show this help`;

function main() {
  const { values } = parseArgs({
    args: process.argv.slice(2),
    options: {
      name: { type: "string", short: "n", default: "reader" },
      times: { type: "string", short: "t", default: "1" },
      upper: { type: "boolean", short: "u", default: false },
      help: { type: "boolean", short: "h", default: false },
    },
    strict: true,
    allowPositionals: false,
  });

  if (values.help) {
    console.log(usage);
    return;
  }

  const name = values.name.trim();
  if (name.length === 0) {
    throw new Error("--name must not be empty.");
  }

  if (!["1", "2", "3", "4", "5"].includes(values.times)) {
    throw new Error("--times must be a digit from 1 to 5.");
  }
  const times = Number(values.times);
  const greeting = `Hello, ${name}!`;

  for (let index = 0; index < times; index += 1) {
    console.log(values.upper ? greeting.toUpperCase() : greeting);
  }
}

try {
  main();
} catch (error) {
  console.error(`Error: ${error.message}`);
  console.error("Try --help for usage.");
  process.exitCode = 2;
}

process.argv starts with the Node executable and script path. slice(2) gives the parser only the arguments after the filename. Each option declares a string or boolean type, with an optional one-letter alias and default. There is no numeric option type here: we accept a string, validate it, and convert it with Number.

For this deliberately small command, the allowed count is exactly one of the strings "1" through "5". That rejects fractions, trailing text, an empty value, and alternative spellings such as "02". Adjust this rule if your own command needs a larger range.

strict: true rejects unknown options and incompatible option syntax. allowPositionals: false rejects unnamed arguments such as a bare Ada. The version-specific parseArgs reference documents these settings.

The two checks serve different purposes. The parser can tell that --times 2x supplies a string, but our program must decide whether that string is an acceptable count. Likewise, --name "" supplies a value even though the value is empty. Keeping these rules together before the loop means invalid input never produces a partial greeting. When you add another option, update its parser description, application checks, and help text together so they stay consistent.

Run the command

First, use all the defaults:

node greet-with-options.mjs

Output:

Hello, reader!

Next, supply a name containing a space and use the shorter option names:

node greet-with-options.mjs -n "Ada Lovelace" -t 2 -u

Output:

HELLO, ADA LOVELACE!
HELLO, ADA LOVELACE!

Bash’s quotes keep the name together as one argument. --upper and -u are switches: their presence means true. Leave them out for false; --upper=false is not accepted by this command.

A string value can also follow an equals sign. This is useful when the value itself begins with a dash:

node greet-with-options.mjs --name=-Ada --times=1

Output:

Hello, -Ada!

Get the help message:

node greet-with-options.mjs --help

Output:

Usage: node greet-with-options.mjs [options]
  -n, --name TEXT   Name to greet (default: reader)
  -t, --times N     Repeat 1 to 5 times (default: 1)
  -u, --upper       Use uppercase
  -h, --help        Show this help

Help and successful greetings exit with status 0. Parsing happens before the help branch, so --help does not make an unknown option valid. After parsing, help skips the name and count checks.

Make failures visible to the shell

Run an invalid count inside an if statement. This lets you inspect the failure even if your shell has set -e enabled:

if node greet-with-options.mjs --times 0; then
  printf 'Unexpected success\n'
else
  printf 'Exit status: %s\n' "$?"
fi

Output, including the error written to standard error:

Error: --times must be a digit from 1 to 5.
Try --help for usage.
Exit status: 2

Try an option with its required value missing:

if node greet-with-options.mjs --name; then
  printf 'Unexpected success\n'
else
  printf 'Exit status: %s\n' "$?"
fi

Output on the tested Node version:

Error: Option '-n, --name <value>' argument missing
Try --help for usage.
Exit status: 2

The catch handles both parser errors and our validation errors. Setting process.exitCode lets Node finish normally with a failure status, rather than forcing an immediate exit that could interrupt pending output. See Node’s exit guidance.

If an option appears more than once, the final value wins with these settings. For a command that collects repeated inputs, investigate multiple: true; do not assume duplicates are automatically rejected.

Clean up

In the same Bash session, remove only the file you created and then the empty directory:

rm -- greet-with-options.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

Acknowledgements

This post was created with assistance from AI.

Last updated on