@listr2/prompt-adapter-inquirer review
@listr2/prompt-adapter-inquirer 4.2.6 lets an Inquirer question run inside a Listr2 task without the prompt and live task renderer writing over each other. Your code imports a function such as `input`, `select`, or `confirm` from `@inquirer/prompts`, then passes it to the adapter through `task.prompt(...).run()`. The adapter routes terminal output through Listr2 and carries cancellation to the active prompt. It provides no questions by itself. Version 4.2.6 only updates its exact Listr2 peer from 11.0.0 to 11.0.1. Our measured 4.2.5 install was Node-only in practice and required Node 22.13.0 or newer.
Our 4.2.5 install took 6.4 seconds, pulled in 43 packages, and could not produce a browser bundle; current 4.2.6 is a Node 22.13+ bridge pinned to Listr2 11.0.1. Install it only when an Inquirer prompt must coexist with Listr2's live renderer.
We installed it
| Install | ✓ · 6.4s | 43 packages on disk · 3 MB |
| Import | ✓ | ESM import works · require() works · ESM package with exports map |
| Browser | n/a | could not be bundled for the browser (Node-only code, most likely) |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does @listr2/prompt-adapter-inquirer install cleanly?
Yes. In a fresh container with an empty cache, npm install @listr2/prompt-adapter-inquirer finished in 6 seconds, leaving 43 packages and 3 MB on disk. npm audit reported no known vulnerabilities.
Can @listr2/prompt-adapter-inquirer 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 @listr2/prompt-adapter-inquirer work with both ESM and CommonJS?
Yes. Both import '@listr2/prompt-adapter-inquirer' and require('@listr2/prompt-adapter-inquirer') worked in Node 22 in our run. The package is published as ESM with an exports map.
Does @listr2/prompt-adapter-inquirer include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
@listr2/prompt-adapter-inquirer or @inquirer/prompts: which should you use?
@inquirer/prompts: Use it directly when no Listr2 renderer owns the terminal. Our 4.2.5 install took 6.4 seconds, pulled in 43 packages, and could not produce a browser bundle; current 4.2.6 is a Node 22.13+ bridge pinned to Listr2 11.0.1.
When should you not use @listr2/prompt-adapter-inquirer?
The program does not use Listr2; calling @inquirer/prompts directly removes this adapter and its version coupling
Use it if
- A Listr2 11 task must ask an Inquirer question while the default renderer is active
- Prompt cancellation should follow a skipped or cancelled task
- TypeScript should infer the option and answer shape from each imported Inquirer prompt function
- The CLI already standardizes on `@inquirer/prompts` and needs its input to appear inside task output
- The program does not use Listr2; calling `@inquirer/prompts` directly removes this adapter and its version coupling
- Production still runs Node 20 or early Node 22; version 4.2.6 requires Node 22.13.0 or newer
- Your Listr2 version is not exactly 11.0.1; the current peer declaration is a pin, not a compatible major range
- Several tasks may ask questions concurrently; Listr2 documents that prompts compete for the same terminal input and output
- The command must always run unattended; CI and redirected stdin need flags or environment values that bypass interactive questions
Setup reality
We installed @listr2/prompt-adapter-inquirer 4.2.5 in a clean Node 22 sandbox. npm succeeded in 6.4 seconds, left 43 packages using 3 MB, and reported 0 known vulnerabilities. The adapter itself was 28 KB unpacked, with 1 direct dependency and 2 peer dependencies. It is ESM with an exports map, although both require() and ESM import worked in our check. Types are bundled. A browser build failed in esbuild, consistent with a terminal-only package.
Install listr2, this adapter, and @inquirer/prompts as a matched set. Version 4.2.5 requires Node 22.13.0 or newer and pins Listr2 11.0.0; current adapter 4.2.6 moves that exact peer to Listr2 11.0.1. Its other peer accepts @inquirer/prompts majors 3 through 8. No credentials or config file are involved, but npm peer resolution will expose a mismatched Listr2 version before the first prompt runs.
Inside an async task, call task.prompt(ListrInquirerPromptAdapter).run(input, options) and await the answer. Calling input(options) directly bypasses Listr2's output routing. Keep any prompting task list at concurrent: false, since 2 live questions share one keyboard and renderer. cancel() aborts the Inquirer promise, so handle rejection only when cancellation is an expected branch.
The failed browser bundle is a useful boundary: keep this adapter in Node CLI entry points and out of shared browser modules. In CI, cron, Docker builds, or piped input, check process.stdin.isTTY and accept a flag or environment variable instead. Non-TTY renderers can repeat prompt updates even when the process technically works, so an explicit non-interactive path is easier to diagnose.
Patterns
Ask for text inside a task prompt-for-text
import {input} from '@inquirer/prompts';
import {ListrInquirerPromptAdapter} from '@listr2/prompt-adapter-inquirer';
ctx.name = await task.prompt(ListrInquirerPromptAdapter).run(input, {message: 'Project name?'});Pass the prompt function itself; invoking `input()` directly writes outside Listr2's renderer.
Require confirmation confirm-action
import {confirm} from '@inquirer/prompts';
const approved = await task.prompt(ListrInquirerPromptAdapter).run(confirm, {message: 'Remove generated files?', default: false});
if (!approved) task.skip('Files kept');The default is `false`, so pressing Enter alone does not approve the removal.
Choose one deployment target select-one
import {select} from '@inquirer/prompts';
const env = await task.prompt(ListrInquirerPromptAdapter).run(select, {message: 'Deploy where?', choices: [{name: 'Staging', value: 'staging'}, {name: 'Production', value: 'prod'}]});The answer is 1 choice value such as `prod`, not the display label.
Collect several features select-many
import {checkbox} from '@inquirer/prompts';
const features = await task.prompt(ListrInquirerPromptAdapter).run(checkbox, {message: 'Enable features', choices: [{name: 'Tests', value: 'test'}, {name: 'Docker', value: 'docker'}], required: true});`required: true` prevents a 0-item result.
Mask a token read-secret
import {password} from '@inquirer/prompts';
const token = await task.prompt(ListrInquirerPromptAdapter).run(password, {message: 'API token', mask: '*'});Masking only changes terminal echo. Keep the returned value out of task titles and logs.
Reject an invalid slug validate-text
const slug = await task.prompt(ListrInquirerPromptAdapter).run(input, {message: 'Slug', validate: value => /^[a-z0-9-]+$/.test(value) || 'Use lowercase letters, digits, and hyphens'});The validator may return an error string; normalize the accepted answer before storing it if needed.
Read a bounded port prompt-for-number
import {number} from '@inquirer/prompts';
const port = await task.prompt(ListrInquirerPromptAdapter).run(number, {message: 'Port', default: 3000, min: 1, max: 65535, required: true});The allowed range is 1 through 65535, and `required` prevents an undefined answer.
Search a long project list search-choices
import {search} from '@inquirer/prompts';
const id = await task.prompt(ListrInquirerPromptAdapter).run(search, {message: 'Project', source: async (term = '') => projects.filter(x => x.name.includes(term)).map(x => ({name: x.name, value: x.id}))});Debounce a network-backed `source`; otherwise each keystroke can start 1 request.
Abort a stalled question cancel-on-timeout
const prompt = task.prompt(ListrInquirerPromptAdapter);
const timer = setTimeout(() => prompt.cancel(), 30_000);
try { ctx.value = await prompt.run(input, {message: 'Value?'}); } finally { clearTimeout(timer); }At 30 seconds `cancel()` rejects the active promise; the timer still needs cleanup after an early answer.
Require input in automation bypass-without-tty
if (process.env.PROJECT_NAME) {
ctx.name = process.env.PROJECT_NAME;
} else if (!process.stdin.isTTY) {
throw new Error('PROJECT_NAME is required without a TTY');
} else {
ctx.name = await task.prompt(ListrInquirerPromptAdapter).run(input, {message: 'Project name?'});
}A non-TTY run takes the explicit environment path instead of waiting on an invisible prompt.
Run prompting tasks one at a time disable-concurrent-prompts
const tasks = new Listr(items, {concurrent: false});
await tasks.run();Two concurrent questions would read from the same terminal, so prompting lists need `concurrent: false`.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| @inquirer/prompts | npm | Use it directly when no Listr2 renderer owns the terminal. |
| prompts | npm | Choose it for a small standalone question flow with no task-list integration. |
| enquirer | npm | Choose it when an existing CLI already uses Enquirer's prompt types and plugins. |
More cli & tooling guides
chalk · commander · 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.

