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.0@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.
npm install @inquirer/select or npm install @inquirer/promptsimport select, { Separator } from "@inquirer/select";For using with the full prompts collection:
import { select, Separator } from "@inquirer/prompts";import select, { Separator } from "@inquirer/select";
const answer = await select({
message: "Select a package manager",
choices: [
{
name: "npm",
value: "npm",
description: "npm is the most popular package manager",
},
{
name: "yarn",
value: "yarn",
description: "yarn is an awesome package manager",
},
new Separator(),
{
name: "pnpm",
value: "pnpm",
disabled: "(pnpm is not available)",
},
],
});
console.log(answer); // "npm" or "yarn"@inquirer/select is built on top of the Inquirer.js ecosystem using:
Main prompt function for creating interactive selection interfaces.
/**
* Creates an interactive selection prompt
* @param config - Configuration object for the prompt
* @returns Promise resolving to the selected choice value
*/
function select<Value, ChoicesObject = ReadonlyArray<string | Separator> | ReadonlyArray<Choice<Value> | Separator>>(
config: SelectConfig<Value, ChoicesObject>
): Promise<Value>;
interface SelectConfig<
Value,
ChoicesObject = ReadonlyArray<string | Separator> | ReadonlyArray<Choice<Value> | Separator>
> {
/** The question message to display */
message: string;
/** Array of choices for selection */
choices: ChoicesObject extends ReadonlyArray<string | Separator>
? ChoicesObject
: ReadonlyArray<Choice<Value> | Separator>;
/** Number of choices to display per page (default: 7) */
pageSize?: number;
/** Whether to loop navigation at start/end (default: true) */
loop?: boolean;
/** Default selected value */
default?: unknown;
/** Custom instruction text */
instructions?: {
navigation: string;
pager: string;
};
/** Theme customization options */
theme?: PartialDeep<Theme<SelectTheme>>;
}Usage Examples:
// Simple string choices
const color = await select({
message: "Pick a color",
choices: ["red", "green", "blue"],
});
// Complex choices with metadata
const framework = await select({
message: "Choose a framework",
choices: [
{
name: "React",
value: "react",
description: "A JavaScript library for building user interfaces",
short: "React",
},
{
name: "Vue.js",
value: "vue",
description: "The Progressive JavaScript Framework",
short: "Vue",
},
{
name: "Angular",
value: "angular",
disabled: "Not available in this project",
},
],
default: "react",
pageSize: 5,
loop: false,
});
// With custom theming
const option = await select({
message: "Select option",
choices: ["Option A", "Option B", "Option C"],
theme: {
helpMode: "always",
indexMode: "number",
style: {
description: (text: string) => `π ${text}`,
},
},
});Visual separators for grouping choices in the selection list.
/**
* Creates a visual separator for grouping choices
* @param separator - Optional custom separator text (default: line)
*/
class Separator {
constructor(separator?: string);
/** Check if an item is a separator */
static isSeparator(choice: any): choice is Separator;
/** The separator text/line */
separator: string;
}Internal utility functions used by the prompt system.
/**
* Checks if a choice item is selectable (not a separator and not disabled)
* @param item - The choice item to check
* @returns True if the item can be selected
*/
function isSelectable<Value>(
item: NormalizedChoice<Value> | Separator
): item is NormalizedChoice<Value>;
/**
* Normalizes choice configurations into a consistent format
* @param choices - Array of choice configurations or strings
* @returns Array of normalized choices and separators
*/
function normalizeChoices<Value>(
choices: ReadonlyArray<string | Separator> | ReadonlyArray<Choice<Value> | Separator>
): Array<NormalizedChoice<Value> | Separator>;Usage Examples:
import select, { Separator } from "@inquirer/select";
const tool = await select({
message: "Choose a development tool",
choices: [
// Frontend tools
{ name: "React", value: "react" },
{ name: "Vue.js", value: "vue" },
new Separator("--- Backend ---"),
// Backend tools
{ name: "Node.js", value: "node" },
{ name: "Python", value: "python" },
new Separator(), // Default separator line
// Other
{ name: "Database", value: "db" },
],
});/** Prompt status indicating current state */
type Status = 'idle' | 'done' | 'loading';interface Choice<Value> {
/** The value returned when this choice is selected */
value: Value;
/** Display name (defaults to string representation of value) */
name?: string;
/** Additional description shown below the choice list when selected */
description?: string;
/** Short name displayed after selection (defaults to name) */
short?: string;
/** Whether choice is disabled, or custom disabled message */
disabled?: boolean | string;
/** Reserved property for type discrimination */
type?: never;
}
/** Internal normalized choice type used by the prompt system */
interface NormalizedChoice<Value> {
/** The value returned when this choice is selected */
value: Value;
/** Display name (always defined after normalization) */
name: string;
/** Additional description shown below the choice list when selected */
description?: string;
/** Short name displayed after selection */
short: string;
/** Whether choice is disabled, or custom disabled message */
disabled: boolean | string;
}interface SelectTheme {
/** Icon configuration */
icon: {
/** Cursor icon for selected item */
cursor: string;
};
/** Style functions for different text elements */
style: {
/** Style function for disabled choices */
disabled: (text: string) => string;
/** Style function for choice descriptions */
description: (text: string) => string;
};
/** When to show help tips */
helpMode: "always" | "never" | "auto";
/** Whether to show choice indices */
indexMode: "hidden" | "number";
}/** Deep partial utility type from @inquirer/type */
type PartialDeep<T> = {
[P in keyof T]?: PartialDeep<T[P]>;
};
/** Base theme interface from @inquirer/core */
interface Theme<ExtensionTheme = {}> {
prefix: string | { idle: string; done: string };
spinner: {
interval: number;
frames: string[];
};
style: {
answer: (text: string) => string;
message: (text: string, status: 'idle' | 'done' | 'loading') => string;
error: (text: string) => string;
help: (text: string) => string;
highlight: (text: string) => string;
};
} & ExtensionTheme;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.
The select prompt supports multiple navigation methods:
/** Thrown when all choices are disabled */
class ValidationError extends Error {
constructor(message: string);
}Common Errors:
ValidationError: "[select prompt] No selectable choices. All choices are disabled." - Thrown when all provided choices have disabled: trueError Prevention:
// Ensure at least one choice is selectable
const choices = [
{ name: "Option A", value: "a", disabled: true },
{ name: "Option B", value: "b" }, // At least one enabled
];
// Validate choices before creating prompt
const selectableChoices = choices.filter(choice => !choice.disabled);
if (selectableChoices.length === 0) {
throw new Error("No selectable choices provided");
}const answer = await select({
message: "Choose language",
choices: ["English", "Spanish", "French"],
instructions: {
navigation: "Use ββ arrows",
pager: "Use ββ to see more options",
},
});const answer = await select({
message: "Select priority",
choices: [
{ name: "High", value: "high", description: "Urgent task" },
{ name: "Medium", value: "medium", description: "Normal task" },
{ name: "Low", value: "low", description: "Can wait" },
],
theme: {
prefix: { idle: "β", done: "β
" },
helpMode: "always",
indexMode: "number",
icon: { cursor: "π" },
style: {
highlight: (text: string) => `\x1b[36m${text}\x1b[0m`, // Cyan
description: (text: string) => `\x1b[90m${text}\x1b[0m`, // Gray
disabled: (text: string) => `\x1b[2m${text}\x1b[0m`, // Dim
},
},
});// For large choice lists, optimize with pagination
const answer = await select({
message: "Select from many options",
choices: largeChoiceArray, // 100+ items
pageSize: 10, // Show 10 at a time
loop: false, // Disable looping for better performance
});