prompts review
prompts 2.4.2 runs interactive questions in Node terminals and resolves one answer object. Its question types cover text, hidden input, numbers, confirmation, lists, single and multiple selection, autocomplete, dates, and toggles. A question can validate or format its value, inspect prior answers, or disappear when its dynamic type returns a falsy value. override supplies named answers from flags, while inject feeds ordered values into tests. The current release dates to 2021 and fixed a regular-expression denial of service. Our browser build failed, and the package contains no TypeScript declarations.
prompts 2.4.2 installed in 0.4 seconds and occupied 1 MB across 3 packages in our sandbox, with 0 audit findings, but it shipped no TypeScript declarations and its latest release is from 2021. Keep it for a settled CommonJS wizard; begin new typed CLIs with @inquirer/prompts or @clack/prompts.
We installed it
| Install | ✓ · 0.4s | 3 packages on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | — | no TypeScript types found |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does prompts install cleanly?
Yes. In a fresh container with an empty cache, npm install prompts finished in 0.4s, leaving 3 packages and 1 MB on disk. npm audit reported no known vulnerabilities.
Can prompts run in a browser?
Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.
Does prompts work with both ESM and CommonJS?
Yes. Both import 'prompts' and require('prompts') worked in Node 22 in our run. The package is published as CommonJS.
Does prompts include TypeScript types?
No type declarations were found in our install, so TypeScript users need their own declarations.
prompts or @inquirer/prompts: which should you use?
@inquirer/prompts: Use it for actively released modular prompts with current TypeScript support. prompts 2.4.2 installed in 0.4 seconds and occupied 1 MB across 3 packages in our sandbox, with 0 audit findings, but it shipped no TypeScript declarations and its latest release is from 2021.
When should you not use prompts?
The same command must work unattended in CI, cron, or piped input and has no separate flags or config path
Discussed on
Use it if
- A small CommonJS setup command needs standard terminal questions with little ceremony
- A later question should be selected or skipped from an earlier answer
- Tests need to answer a wizard deterministically through prompts.inject
- Flags should prefill named questions through prompts.override before any terminal interaction
- The same command must work unattended in CI, cron, or piped input and has no separate flags or config path
- Release activity and quick fixes are required; 2.4.2 was published in October 2021 and the last repository push was in May 2025
- First-party TypeScript declarations are mandatory; our package inspection found none
- The questions belong in a browser form; our esbuild browser attempt failed on Node terminal code
- Cancellation must throw and discard all state; prompts can resolve the answers collected before the user canceled
Setup reality
We installed prompts 2.4.2 in our fresh Node 22 sandbox in 0.4 seconds. It left 3 packages using 1 MB, and npm audit found 0 known vulnerabilities. The package itself is 412 KB unpacked with 2 direct dependencies, 0 peers, and an MIT license. It declares Node 6 or newer in package metadata, but the README now asks for Node 14+. No TypeScript declarations were present, so typed projects need community types or a local wrapper.
There are no credentials or config files. There must be an interactive stdin and writable terminal output. Check process.stdin.isTTY before opening a question, then use flags, environment variables, or a config file for automation. The question name becomes the response key; duplicate names overwrite values. If a dynamic type returns null, the question contributes no key. Validate the final answer object before a command performs writes, even when each visible prompt validates its own field.
prompts is CommonJS without an exports map. require() and ESM import both worked on our Node 22 box, but ESM receives CommonJS interop rather than native named exports. The esbuild browser build failed. inject uses a module-level queue and consumes values in order, so tests sharing one process can leak answers unless they set up each case carefully. override fills by question name and can bypass the interactive path; validate overridden values at the application boundary.
Cancellation stops the chain and may return a partial object. Escape, Ctrl+C, or Ctrl+D should lead to an explicit canceled state before any side effect. onCancel can return false to stop, but callers still need required-key checks. Async validation and autocomplete suggestions run during interaction. A remote suggest function can issue a request for every keystroke unless you cache, debounce, or search a local list. prompts provides the terminal controls, not a concurrency or retry policy for callbacks.
Patterns
Collect one text answer ask-text
const prompts = require('prompts');
const { project } = await prompts({
type: 'text', name: 'project', message: 'Project name?', initial: 'my-app'
});The question name becomes 1 response key; reusing that name later overwrites the earlier value.
Run a sequence of questions ask-chain
const answers = await prompts([
{ type: 'text', name: 'user', message: 'User?' },
{ type: 'password', name: 'token', message: 'Token?' },
{ type: 'confirm', name: 'save', message: 'Save?', initial: false }
]);Questions run in order, and cancellation can leave a partial object with only the earlier keys.
Mark terminal cancellation stop-on-cancel
let cancelled = false;
const answers = await prompts(questions, { onCancel() { cancelled = true; return false; } });
if (cancelled) { process.exitCode = 130; return; }onCancel stops the chain, while the explicit flag prevents partial answers from reaching side effects.
Skip a question from the prior answer skip-dependent
const questions = [
{ type: 'confirm', name: 'typescript', message: 'Use TypeScript?' },
{ type: prev => prev ? 'select' : null, name: 'mode', message: 'Mode?', choices: [{ title: 'Strict', value: 'strict' }] }
];Returning null creates 0 key for mode, so the caller must supply its own default.
Reject invalid text in place validate-value
const question = {
type: 'text', name: 'email', message: 'Email?',
validate: value => /.+@.+\..+/.test(value) || 'Enter a valid email'
};Return true to accept or a string to display a validation message beside the current prompt.
Return one stable choice value select-one
const question = {
type: 'select', name: 'manager', message: 'Package manager?', initial: 1,
choices: [{ title: 'npm', value: 'npm' }, { title: 'pnpm', value: 'pnpm' }]
};initial is the zero-based choice index, so 1 selects pnpm rather than matching a value.
Return several choice values select-many
const question = {
type: 'multiselect', name: 'features', message: 'Features?',
choices: [{ title: 'Lint', value: 'lint', selected: true }, { title: 'Test', value: 'test' }]
};The answer is an array, including an empty array when the user selects 0 choices.
Normalize accepted input format-answer
const question = {
type: 'text', name: 'slug', message: 'Slug?',
format: value => value.trim().toLowerCase().replace(/\s+/g, '-')
};validate sees the entered value, then format controls the value stored in the answer object.
Answer prompts without a terminal inject-tests
const prompts = require('prompts');
prompts.inject(['my-app', true]);
const result = await runWizard();inject consumes 2 queued values globally in this example, so each test must prepare its own queue.
Fill named questions from flags override-flags
prompts.override({ project: 'api', force: true });
const answers = await prompts(questions);override bypasses interactive entry for matching names; validate the completed answer object afterward.
Refuse a noninteractive session guard-tty
if (!process.stdin.isTTY) {
throw new Error('Interactive terminal required; pass --config instead.');
}
const answers = await prompts(questions);This check turns an unattended wait into an immediate failure before 1 prompt opens.
Write prompts to stderr custom-streams
const answer = await prompts({
type: 'text', name: 'value', message: 'Value?',
stdin: process.stdin, stdout: process.stderr
});Using stderr keeps stdout clean for machine-readable command output such as JSON.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @inquirer/prompts | npm | Use it for actively released modular prompts with current TypeScript support |
| enquirer | npm | Use it when forms, surveys, sorting, and custom prompt subclasses matter |
| @clack/prompts | npm | Use it for grouped CLI flows with status messages, spinners, and explicit cancellation |
More cli & tooling guides
commander · chalk · typescript · esbuild · yargs · click · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

