@clack/prompts
Build beautiful, minimal interactive CLI apps. Pre-styled, tree-shakeable, 80% smaller than alternatives.
Quick Start
npm install @clack/prompts
import { intro, outro, text, isCancel, cancel } from '@clack/prompts';
intro('create-my-app');
const name = await text({ message: 'Project name?' });
if (isCancel(name)) { cancel('Cancelled.'); process.exit(0); }
outro(`Done! Created ${name}.`);
Core Pattern — Always Guard Cancellation
Every prompt can return a cancel symbol (user pressed Ctrl+C). Always use isCancel():
const result = await text({ message: 'Name?' });
if (isCancel(result)) {
cancel('Cancelled.');
process.exit(0);
}
Choosing the Right Component
| Need | Component | Returns |
|---|---|---|
| Single-line text | text | string |
| Masked secret | password | string |
| Yes / No | confirm | boolean |
| Pick one from list | select | string (value) |
| Searchable pick one | autocomplete | string (value) |
| Single-key selection | selectKey | string (value) |
| Pick many from list | multiselect | string[] |
| Pick many, grouped | groupMultiselect | string[] |
| Multi-line text area | multiline | string |
| Filesystem path | path | string |
| Calendar date | date | Date |
Common Patterns
Sequential Prompts with group()
Chain prompts where later ones depend on earlier answers:
import * as p from '@clack/prompts';
const result = await p.group(
{
name: () => p.text({ message: 'What is your name?' }),
age: () => p.text({ message: 'What is your age?' }),
color: ({ results }) =>
p.multiselect({
message: `What is your favorite color ${results.name}?`,
options: [
{ value: 'red', label: 'Red' },
{ value: 'blue', label: 'Blue' },
],
}),
},
{
onCancel: ({ results }) => {
p.cancel('Operation cancelled.');
process.exit(0);
},
},
);
Task Runner with Spinners
Run sequential tasks, each with its own spinner:
import { tasks } from '@clack/prompts';
await tasks([
{
title: 'Installing via npm',
task: async () => {
// Return a string = success with checkmark
return 'Installed via npm';
},
},
{
title: 'Running tests',
task: async () => {
throw new Error('Tests failed!'); // Error = red X
},
},
]);
Standalone Spinner
import { spinner } from '@clack/prompts';
const s = spinner();
s.start('Uploading...');
try { await upload(); s.stop('Uploaded!'); }
catch { s.stop('Upload failed'); }
Progress Bar
const p = progress({ max: 100 });
p.start('Downloading');
for (const chunk of chunks) { p.advance(1); }
p.stop('Done');
Live Sub-Process Output with taskLog
import { taskLog } from '@clack/prompts';
const log = taskLog({ title: 'npm install' });
for await (const line of npmInstall()) { log.message(line); }
log.success('Done!'); // Clears output on success
// log.error('Failed!'); // Keeps output visible on failure
Styled Logging (no user input)
import { log } from '@clack/prompts';
import color from 'picocolors';
log.info('Starting setup...');
log.success('Config written!');
log.step('Checking dependencies...');
log.warn('Optional peer dep missing');
log.error('Build failed');
log.message('Custom', { symbol: color.cyan('~') });
Streaming Logs
For LLM output or dynamic streams:
import { stream } from '@clack/prompts';
stream.info((function* () { yield 'Info!'; })());
stream.success((async function* () { yield 'Done!'; })());
stream.message(
(function* () { yield 'Hello'; yield ', World'; })(),
{ symbol: color.cyan('~') }
);
Selection Options Shape
Used by select, autocomplete, selectKey, multiselect, groupMultiselect:
{ value: 'ts', label: 'TypeScript' } // Basic
{ value: 'js', label: 'JavaScript', disabled: true } // Disabled
{ value: 'coffee', label: 'CoffeeScript', hint: 'oh no' } // With hint
Validation
Most components accept a validate(value) that returns string (error message) or undefined (valid):
const name = await text({
message: 'Username?',
validate(value) {
if (!value.length) return 'Username is required!';
if (value.length < 3) return 'At least 3 characters.';
},
});
Full API Reference
See references/api.md for every component's complete signature, options table, and examples.