# `@clack/prompts`

Effortlessly build beautiful command-line apps 🪄 [Try the demo](https://stackblitz.com/edit/clack-prompts?file=index.js)

![clack-prompt](https://github.com/bombshell-dev/clack/blob/main/.github/assets/clack-demo.gif)

---

`@clack/prompts` is an opinionated, pre-styled wrapper around [`@clack/core`](https://www.npmjs.com/package/@clack/core).

- 🤏 80% smaller than other options
- 💎 Beautiful, minimal UI
- ✅ Simple API
- 🧱 Comes with `text`, `password`, `confirm`, `date`, `select`, `autocomplete`, `selectKey`, `multiselect`, `path`, and `spinner` components

## Basics

### Setup

The `intro` and `outro` functions will print a message to begin or end a prompt session, respectively.

```js
import { intro, outro } from '@clack/prompts';

intro(`create-my-app`);
// Do stuff
outro(`You're all set!`);
```

### Cancellation

The `isCancel` function is a guard that detects when a user cancels a question with `CTRL + C`. You should handle this situation for each prompt, optionally providing a nice cancellation message with the `cancel` utility.

```js
import { isCancel, cancel, text } from '@clack/prompts';

const value = await text({
  message: 'What is the meaning of life?',
});

if (isCancel(value)) {
  cancel('Operation cancelled.');
  process.exit(0);
}
```

## Components

### Text

The text component accepts a single line of text.

```js
import { text } from '@clack/prompts';

const meaning = await text({
  message: 'What is the meaning of life?',
  placeholder: 'Not sure',
  initialValue: '42',
  validate(value) {
    if (value.length === 0) return `Value is required!`;
  },
});
```

### Password

The password component behaves like `text`, but masks the input as the user types.

```js
import { password } from '@clack/prompts';

const secret = await password({
  message: 'Set a password.',
  mask: '*',
  validate(value) {
    if (!value || value.length < 8) return 'Password must be at least 8 characters.';
  },
});
```

### Confirm

The confirm component accepts a yes or no answer. The result is a boolean value of `true` or `false`.

```js
import { confirm } from '@clack/prompts';

const shouldContinue = await confirm({
  message: 'Do you want to continue?',
});
```

### Date

The date component accepts a calendar date and returns a `Date` value.

```js
import { date } from '@clack/prompts';

const dueDate = await date({
  message: 'Pick a due date.',
  format: 'YMD',
  minDate: new Date(Date.UTC(2026, 0, 1)),
  maxDate: new Date(Date.UTC(2026, 11, 31)),
});
```

### Select

The select component allows a user to choose one value from a list of options. The result is the `value` prop of a given option.

```js
import { select } from '@clack/prompts';

const projectType = await select({
  message: 'Pick a project type.',
  options: [
    { value: 'ts', label: 'TypeScript' },
    { value: 'js', label: 'JavaScript', disabled: true },
    { value: 'coffee', label: 'CoffeeScript', hint: 'oh no' },
  ],
});
```

### Autocomplete

The autocomplete component lets a user filter a list by typing, then choose one option from the matching results. By default, matching uses each option's `label`, `hint`, and `value`. The result is the selected option's `value`.

```js
import { autocomplete } from '@clack/prompts';

const framework = await autocomplete({
  message: 'Pick a framework.',
  placeholder: 'Type to search...',
  options: [
    { value: 'next', label: 'Next.js' },
    { value: 'nuxt', label: 'Nuxt' },
    { value: 'sveltekit', label: 'SvelteKit' },
    { value: 'remix', label: 'Remix' },
  ],
});
```

### Select Key

The `selectKey` component lets a user choose an option by pressing its single-character string `value` key directly.

```js
import { selectKey } from '@clack/prompts';

const action = await selectKey({
  message: 'Pick an action.',
  options: [
    { value: 'd', label: 'Deploy' },
    { value: 't', label: 'Run tests' },
    { value: 'q', label: 'Quit' },
  ],
});
```

### Multi-Select

The `multiselect` component allows a user to choose many values from a list of options. The result is an array with all selected `value` props.

```js
import { multiselect } from '@clack/prompts';

const additionalTools = await multiselect({
  message: 'Select additional tools.',
  options: [
    { value: 'eslint', label: 'ESLint', hint: 'recommended' },
    { value: 'prettier', label: 'Prettier', disabled: true },
    { value: 'gh-action', label: 'GitHub Action' },
  ],
  required: false,
});
```

It is also possible to select multiple items arranged into hierarchy by using `groupMultiselect`:

```js
import { groupMultiselect } from '@clack/prompts';

const basket = await groupMultiselect({
  message: 'Select your favorite fruits and vegetables:',
  options: {
    fruits: [
      { value: 'apple', label: 'apple' },
      { value: 'banana', label: 'banana' },
      { value: 'cherry', label: 'cherry' },
    ],
    vegetables: [
      { value: 'carrot', label: 'carrot' },
      { value: 'spinach', label: 'spinach' },
      { value: 'potato', label: 'potato' },
    ]
  }
});
```

### Multi-Line Text

The multi-line text component accepts multiple lines of text input. By default, pressing `Enter` twice submits the input.

```js
import { multiline } from '@clack/prompts';

const bio = await multiline({
  message: 'Tell us about yourself.',
  placeholder: 'Start typing...',
  validate(value) {
    if (value.length === 0) return `value is required`;
  },
});
```

Set `showSubmit` to display an explicit submit button instead of double `Enter` submission:

```js
const bio = await multiline({
  message: 'Tell us about yourself.',
  showSubmit: true,
});
```

### Path

The path component offers filesystem path suggestions and returns the selected path as a string. When `directory: true` is set, only directories can be selected.

```js
import { path } from '@clack/prompts';

const targetDir = await path({
  message: 'Select an existing directory.',
  directory: true,
});
```

### Spinner

The spinner component surfaces a pending action, such as a long-running download or dependency installation.

```js
import { spinner } from '@clack/prompts';

const s = spinner();
s.start('Installing via npm');
// Do installation here
s.stop('Installed via npm');
```

### Progress

The progress component extends the spinner component to add a progress bar to visualize the progression of an action.

```js
import { progress } from '@clack/prompts';

const p = progress({ max: 10 });
p.start('Downloading archive');
// Do download here
p.advance(3, 'Downloading (30%)');
// ...
p.advance(5, 'Downloading (80%)');
// ...
p.stop('Archive downloaded');
```

## Utilities

### Grouping

Grouping prompts together is a great way to keep your code organized. This accepts a JSON object with a name that can be used to reference the group later. The second argument is an optional but has a `onCancel` callback that will be called if the user cancels one of the prompts in the group.

```js
import * as p from '@clack/prompts';

const group = 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: 'green', label: 'Green' },
          { value: 'blue', label: 'Blue' },
        ],
      }),
  },
  {
    // On Cancel callback that wraps the group
    // So if the user cancels one of the prompts in the group this function will be called
    onCancel: ({ results }) => {
      p.cancel('Operation cancelled.');
      process.exit(0);
    },
  }
);

console.log(group.name, group.age, group.color);
```

### Tasks

Execute multiple tasks in spinners.

```js
import { tasks } from '@clack/prompts';

await tasks([
  {
    title: 'Installing via npm',
    task: async (message) => {
      // Do installation here
      return 'Installed via npm';
    },
  },
]);
```

### Logs

```js
import { log } from '@clack/prompts';

log.info('Info!');
log.success('Success!');
log.step('Step!');
log.warn('Warn!');
log.error('Error!');
log.message('Hello, World', { symbol: color.cyan('~') });
```


### Stream

When interacting with dynamic LLMs or other streaming message providers, use the `stream` APIs to log messages from an iterable, even an async one.

```js
import { stream } from '@clack/prompts';

stream.info((function *() { yield 'Info!'; })());
stream.success((function *() { yield 'Success!'; })());
stream.step((function *() { yield 'Step!'; })());
stream.warn((function *() { yield 'Warn!'; })());
stream.error((function *() { yield 'Error!'; })());
stream.message((function *() { yield 'Hello'; yield ", World" })(), { symbol: color.cyan('~') });
```

![clack-log-prompts](https://github.com/bombshell-dev/clack/blob/main/.github/assets/clack-logs.png)

### Task Log

When executing a sub-process or a similar sub-task, `taskLog` can be used to render the output continuously and clear it at the end if it was successful.

```js
import { taskLog } from '@clack/prompts';

const log = taskLog({
	title: 'Running npm install'
});

for await (const line of npmInstall()) {
	log.message(line);
}

if (success) {
	log.success('Done!');
} else {
	log.error('Failed!');
}
```
