CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-inquirer--select

Interactive command line select/list prompt component with customizable theming, pagination, search, and navigation features

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

@inquirer/select

@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.

Package Information

  • Package Name: @inquirer/select
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @inquirer/select or npm install @inquirer/prompts

Core Imports

import select, { Separator } from "@inquirer/select";

For using with the full prompts collection:

import { select, Separator } from "@inquirer/prompts";

Basic Usage

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"

Architecture

@inquirer/select is built on top of the Inquirer.js ecosystem using:

  • React-like Hooks: Uses useState, useKeypress, and other hooks from @inquirer/core for state management
  • Prompt System: Built with createPrompt from @inquirer/core providing consistent prompt interface
  • Theming Engine: Flexible theming system with customizable colors, icons, and display modes
  • Pagination: Built-in pagination for handling long choice lists
  • Navigation Engine: Comprehensive keyboard navigation including arrow keys, number selection, and search

Capabilities

Select Prompt

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}`,
    },
  },
});

Choice Separators

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;
}

Utility Functions

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" },
  ],
});

Types

Status Types

/** Prompt status indicating current state */
type Status = 'idle' | 'done' | 'loading';

Choice Configuration

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;
}

Theme Configuration

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";
}

Dependency Types

/** 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.

Navigation Features

The select prompt supports multiple navigation methods:

Keyboard Navigation

  • Arrow Keys: Navigate up/down through choices
  • Enter: Select current choice
  • Number Keys: Jump to choice by index (1-9, multi-digit supported)
  • Letter Keys: Search choices by typing (case-insensitive)
  • Backspace: Clear search input

Search Functionality

  • Type any text to search through choice names
  • Search is case-insensitive and matches from the beginning of choice names
  • Search timeout of 700ms clears the search automatically
  • Backspace clears current search input

Pagination

  • Configurable page size (default: 7 choices per page)
  • Automatic pagination for long choice lists
  • Help text indicates when more choices are available
  • Smooth scrolling when navigating beyond visible choices

Loop Navigation

  • Configurable looping (default: enabled)
  • When enabled: navigation wraps from last to first choice and vice versa
  • When disabled: navigation stops at first/last selectable choice

Accessibility Features

  • Keyboard-only navigation: Full functionality without mouse
  • Screen reader support: Proper ARIA labels and semantic structure
  • Visual indicators: Clear cursor positioning and selection states
  • Status feedback: Visual confirmation of selection and completion
  • Disabled choice handling: Proper skipping and visual indication of disabled choices
  • Separator handling: Non-selectable separators properly skipped during navigation

Error Handling

/** 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: true

Error 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");
}

Advanced Configuration

Custom Instructions

const answer = await select({
  message: "Choose language",
  choices: ["English", "Spanish", "French"],
  instructions: {
    navigation: "Use ↑↓ arrows",
    pager: "Use ↑↓ to see more options",
  },
});

Advanced Theming

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
    },
  },
});

Performance Optimization

// 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
});

docs

index.md

tile.json