Interactive command line select/list prompt component with customizable theming, pagination, search, and navigation features
npx @tessl/cli install tessl/npm-inquirer--select@4.3.00
# @inquirer/select
1
2
@inquirer/select provides an interactive command line selection prompt component that displays a list of choices for single selection. It offers comprehensive customization options including keyboard navigation, search functionality, theming support, pagination, and accessibility features.
3
4
## Package Information
5
6
- **Package Name**: @inquirer/select
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install @inquirer/select` or `npm install @inquirer/prompts`
10
11
## Core Imports
12
13
```typescript
14
import select, { Separator } from "@inquirer/select";
15
```
16
17
For using with the full prompts collection:
18
19
```typescript
20
import { select, Separator } from "@inquirer/prompts";
21
```
22
23
## Basic Usage
24
25
```typescript
26
import select, { Separator } from "@inquirer/select";
27
28
const answer = await select({
29
message: "Select a package manager",
30
choices: [
31
{
32
name: "npm",
33
value: "npm",
34
description: "npm is the most popular package manager",
35
},
36
{
37
name: "yarn",
38
value: "yarn",
39
description: "yarn is an awesome package manager",
40
},
41
new Separator(),
42
{
43
name: "pnpm",
44
value: "pnpm",
45
disabled: "(pnpm is not available)",
46
},
47
],
48
});
49
50
console.log(answer); // "npm" or "yarn"
51
```
52
53
## Architecture
54
55
@inquirer/select is built on top of the Inquirer.js ecosystem using:
56
57
- **React-like Hooks**: Uses useState, useKeypress, and other hooks from @inquirer/core for state management
58
- **Prompt System**: Built with createPrompt from @inquirer/core providing consistent prompt interface
59
- **Theming Engine**: Flexible theming system with customizable colors, icons, and display modes
60
- **Pagination**: Built-in pagination for handling long choice lists
61
- **Navigation Engine**: Comprehensive keyboard navigation including arrow keys, number selection, and search
62
63
## Capabilities
64
65
### Select Prompt
66
67
Main prompt function for creating interactive selection interfaces.
68
69
```typescript { .api }
70
/**
71
* Creates an interactive selection prompt
72
* @param config - Configuration object for the prompt
73
* @returns Promise resolving to the selected choice value
74
*/
75
function select<Value, ChoicesObject = ReadonlyArray<string | Separator> | ReadonlyArray<Choice<Value> | Separator>>(
76
config: SelectConfig<Value, ChoicesObject>
77
): Promise<Value>;
78
79
interface SelectConfig<
80
Value,
81
ChoicesObject = ReadonlyArray<string | Separator> | ReadonlyArray<Choice<Value> | Separator>
82
> {
83
/** The question message to display */
84
message: string;
85
/** Array of choices for selection */
86
choices: ChoicesObject extends ReadonlyArray<string | Separator>
87
? ChoicesObject
88
: ReadonlyArray<Choice<Value> | Separator>;
89
/** Number of choices to display per page (default: 7) */
90
pageSize?: number;
91
/** Whether to loop navigation at start/end (default: true) */
92
loop?: boolean;
93
/** Default selected value */
94
default?: unknown;
95
/** Custom instruction text */
96
instructions?: {
97
navigation: string;
98
pager: string;
99
};
100
/** Theme customization options */
101
theme?: PartialDeep<Theme<SelectTheme>>;
102
}
103
```
104
105
**Usage Examples:**
106
107
```typescript
108
// Simple string choices
109
const color = await select({
110
message: "Pick a color",
111
choices: ["red", "green", "blue"],
112
});
113
114
// Complex choices with metadata
115
const framework = await select({
116
message: "Choose a framework",
117
choices: [
118
{
119
name: "React",
120
value: "react",
121
description: "A JavaScript library for building user interfaces",
122
short: "React",
123
},
124
{
125
name: "Vue.js",
126
value: "vue",
127
description: "The Progressive JavaScript Framework",
128
short: "Vue",
129
},
130
{
131
name: "Angular",
132
value: "angular",
133
disabled: "Not available in this project",
134
},
135
],
136
default: "react",
137
pageSize: 5,
138
loop: false,
139
});
140
141
// With custom theming
142
const option = await select({
143
message: "Select option",
144
choices: ["Option A", "Option B", "Option C"],
145
theme: {
146
helpMode: "always",
147
indexMode: "number",
148
style: {
149
description: (text: string) => `π ${text}`,
150
},
151
},
152
});
153
```
154
155
### Choice Separators
156
157
Visual separators for grouping choices in the selection list.
158
159
```typescript { .api }
160
/**
161
* Creates a visual separator for grouping choices
162
* @param separator - Optional custom separator text (default: line)
163
*/
164
class Separator {
165
constructor(separator?: string);
166
167
/** Check if an item is a separator */
168
static isSeparator(choice: any): choice is Separator;
169
170
/** The separator text/line */
171
separator: string;
172
}
173
```
174
175
### Utility Functions
176
177
Internal utility functions used by the prompt system.
178
179
```typescript { .api }
180
/**
181
* Checks if a choice item is selectable (not a separator and not disabled)
182
* @param item - The choice item to check
183
* @returns True if the item can be selected
184
*/
185
function isSelectable<Value>(
186
item: NormalizedChoice<Value> | Separator
187
): item is NormalizedChoice<Value>;
188
189
/**
190
* Normalizes choice configurations into a consistent format
191
* @param choices - Array of choice configurations or strings
192
* @returns Array of normalized choices and separators
193
*/
194
function normalizeChoices<Value>(
195
choices: ReadonlyArray<string | Separator> | ReadonlyArray<Choice<Value> | Separator>
196
): Array<NormalizedChoice<Value> | Separator>;
197
```
198
199
**Usage Examples:**
200
201
```typescript
202
import select, { Separator } from "@inquirer/select";
203
204
const tool = await select({
205
message: "Choose a development tool",
206
choices: [
207
// Frontend tools
208
{ name: "React", value: "react" },
209
{ name: "Vue.js", value: "vue" },
210
211
new Separator("--- Backend ---"),
212
213
// Backend tools
214
{ name: "Node.js", value: "node" },
215
{ name: "Python", value: "python" },
216
217
new Separator(), // Default separator line
218
219
// Other
220
{ name: "Database", value: "db" },
221
],
222
});
223
```
224
225
## Types
226
227
### Status Types
228
229
```typescript { .api }
230
/** Prompt status indicating current state */
231
type Status = 'idle' | 'done' | 'loading';
232
```
233
234
### Choice Configuration
235
236
```typescript { .api }
237
interface Choice<Value> {
238
/** The value returned when this choice is selected */
239
value: Value;
240
/** Display name (defaults to string representation of value) */
241
name?: string;
242
/** Additional description shown below the choice list when selected */
243
description?: string;
244
/** Short name displayed after selection (defaults to name) */
245
short?: string;
246
/** Whether choice is disabled, or custom disabled message */
247
disabled?: boolean | string;
248
/** Reserved property for type discrimination */
249
type?: never;
250
}
251
252
/** Internal normalized choice type used by the prompt system */
253
interface NormalizedChoice<Value> {
254
/** The value returned when this choice is selected */
255
value: Value;
256
/** Display name (always defined after normalization) */
257
name: string;
258
/** Additional description shown below the choice list when selected */
259
description?: string;
260
/** Short name displayed after selection */
261
short: string;
262
/** Whether choice is disabled, or custom disabled message */
263
disabled: boolean | string;
264
}
265
```
266
267
### Theme Configuration
268
269
```typescript { .api }
270
interface SelectTheme {
271
/** Icon configuration */
272
icon: {
273
/** Cursor icon for selected item */
274
cursor: string;
275
};
276
/** Style functions for different text elements */
277
style: {
278
/** Style function for disabled choices */
279
disabled: (text: string) => string;
280
/** Style function for choice descriptions */
281
description: (text: string) => string;
282
};
283
/** When to show help tips */
284
helpMode: "always" | "never" | "auto";
285
/** Whether to show choice indices */
286
indexMode: "hidden" | "number";
287
}
288
```
289
290
### Dependency Types
291
292
```typescript { .api }
293
/** Deep partial utility type from @inquirer/type */
294
type PartialDeep<T> = {
295
[P in keyof T]?: PartialDeep<T[P]>;
296
};
297
298
/** Base theme interface from @inquirer/core */
299
interface Theme<ExtensionTheme = {}> {
300
prefix: string | { idle: string; done: string };
301
spinner: {
302
interval: number;
303
frames: string[];
304
};
305
style: {
306
answer: (text: string) => string;
307
message: (text: string, status: 'idle' | 'done' | 'loading') => string;
308
error: (text: string) => string;
309
help: (text: string) => string;
310
highlight: (text: string) => string;
311
};
312
} & ExtensionTheme;
313
```
314
315
**Note**: The `theme` parameter accepts `PartialDeep<Theme<SelectTheme>>` where `Theme<SelectTheme>` extends the base theme from `@inquirer/core` with `SelectTheme` properties. The `PartialDeep<T>` type is imported from `@inquirer/type` and allows for deep partial customization of theme properties.
316
317
## Navigation Features
318
319
The select prompt supports multiple navigation methods:
320
321
### Keyboard Navigation
322
- **Arrow Keys**: Navigate up/down through choices
323
- **Enter**: Select current choice
324
- **Number Keys**: Jump to choice by index (1-9, multi-digit supported)
325
- **Letter Keys**: Search choices by typing (case-insensitive)
326
- **Backspace**: Clear search input
327
328
### Search Functionality
329
- Type any text to search through choice names
330
- Search is case-insensitive and matches from the beginning of choice names
331
- Search timeout of 700ms clears the search automatically
332
- Backspace clears current search input
333
334
### Pagination
335
- Configurable page size (default: 7 choices per page)
336
- Automatic pagination for long choice lists
337
- Help text indicates when more choices are available
338
- Smooth scrolling when navigating beyond visible choices
339
340
### Loop Navigation
341
- Configurable looping (default: enabled)
342
- When enabled: navigation wraps from last to first choice and vice versa
343
- When disabled: navigation stops at first/last selectable choice
344
345
## Accessibility Features
346
347
- **Keyboard-only navigation**: Full functionality without mouse
348
- **Screen reader support**: Proper ARIA labels and semantic structure
349
- **Visual indicators**: Clear cursor positioning and selection states
350
- **Status feedback**: Visual confirmation of selection and completion
351
- **Disabled choice handling**: Proper skipping and visual indication of disabled choices
352
- **Separator handling**: Non-selectable separators properly skipped during navigation
353
354
## Error Handling
355
356
```typescript { .api }
357
/** Thrown when all choices are disabled */
358
class ValidationError extends Error {
359
constructor(message: string);
360
}
361
```
362
363
**Common Errors:**
364
365
- `ValidationError`: "[select prompt] No selectable choices. All choices are disabled." - Thrown when all provided choices have `disabled: true`
366
367
**Error Prevention:**
368
369
```typescript
370
// Ensure at least one choice is selectable
371
const choices = [
372
{ name: "Option A", value: "a", disabled: true },
373
{ name: "Option B", value: "b" }, // At least one enabled
374
];
375
376
// Validate choices before creating prompt
377
const selectableChoices = choices.filter(choice => !choice.disabled);
378
if (selectableChoices.length === 0) {
379
throw new Error("No selectable choices provided");
380
}
381
```
382
383
## Advanced Configuration
384
385
### Custom Instructions
386
387
```typescript
388
const answer = await select({
389
message: "Choose language",
390
choices: ["English", "Spanish", "French"],
391
instructions: {
392
navigation: "Use ββ arrows",
393
pager: "Use ββ to see more options",
394
},
395
});
396
```
397
398
### Advanced Theming
399
400
```typescript
401
const answer = await select({
402
message: "Select priority",
403
choices: [
404
{ name: "High", value: "high", description: "Urgent task" },
405
{ name: "Medium", value: "medium", description: "Normal task" },
406
{ name: "Low", value: "low", description: "Can wait" },
407
],
408
theme: {
409
prefix: { idle: "β", done: "β " },
410
helpMode: "always",
411
indexMode: "number",
412
icon: { cursor: "π" },
413
style: {
414
highlight: (text: string) => `\x1b[36m${text}\x1b[0m`, // Cyan
415
description: (text: string) => `\x1b[90m${text}\x1b[0m`, // Gray
416
disabled: (text: string) => `\x1b[2m${text}\x1b[0m`, // Dim
417
},
418
},
419
});
420
```
421
422
### Performance Optimization
423
424
```typescript
425
// For large choice lists, optimize with pagination
426
const answer = await select({
427
message: "Select from many options",
428
choices: largeChoiceArray, // 100+ items
429
pageSize: 10, // Show 10 at a time
430
loop: false, // Disable looping for better performance
431
});
432
```