Listr2 - Interactive Terminal Task Lists

10.2.1 · active · verified Wed Apr 22

Listr2 is a robust and highly interactive task list manager for Node.js command-line applications, designed to provide live updates and a dynamic user experience. As of version 10.2.1, it emphasizes modern JavaScript practices and is primarily used with TypeScript, shipping with comprehensive type definitions. It evolved from the original Listr package, offering a rewritten architecture for improved flexibility, extensibility, and performance, including advanced error handling, concurrent task execution, and support for multiple renderers (e.g., default, verbose, silent). Its release cadence is active, with frequent updates addressing bug fixes, dependency updates, and new features, often released in minor and patch versions. Key differentiators include its ability to manage complex multi-level tasks with live output, integration with popular prompt libraries like Inquirer.js and Enquirer through dedicated adapters, and a flexible API for customizability, making it suitable for complex CLI workflows.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates creating a new Listr instance with main tasks and subtasks, handling context, displaying output, and managing errors with both sequential and concurrent execution.

import { Listr, ListrDefaultRenderer } from 'listr2';

interface MyContext {
  input: string;
  output: string;
}

async function runTasks() {
  const tasks = new Listr<MyContext>(
    [
      {
        title: 'Main task 1: Processing input',
        task: async (ctx, task) => {
          ctx.input = 'initial data';
          task.output = `Input set to: ${ctx.input}`;
          await new Promise((resolve) => setTimeout(resolve, 1000));
        }
      },
      {
        title: 'Main task 2: With subtasks',
        task: (ctx, task) =>
          task.newListr(
            [
              {
                title: 'Subtask 2.1: Fetching data',
                task: async (_, subtask) => {
                  subtask.output = 'Fetching from external API...';
                  await new Promise((resolve) => setTimeout(resolve, 1500));
                  // Simulate an error for demonstration
                  if (Math.random() > 0.7) {
                    throw new Error('Failed to fetch data!');
                  }
                }
              },
              {
                title: 'Subtask 2.2: Transforming data',
                task: async (_, subtask) => {
                  subtask.output = 'Applying transformations.';
                  await new Promise((resolve) => setTimeout(resolve, 800));
                }
              }
            ],
            { concurrent: true, exitOnError: false }
          )
      },
      {
        title: 'Main task 3: Generating output',
        task: async (ctx, task) => {
          ctx.output = `Processed: ${ctx.input.toUpperCase()}`;
          task.output = `Final output: ${ctx.output}`;
          await new Promise((resolve) => setTimeout(resolve, 500));
        },
        options: { persistentOutput: true }
      }
    ],
    {
      concurrent: false,
      exitOnError: true,
      renderer: ListrDefaultRenderer,
      rendererOptions: {
        collapse: false,
        clearOutput: true
      }
    }
  );

  try {
    const context = await tasks.run();
    console.log('\nAll tasks completed successfully!');
    console.log('Final Context:', context);
  } catch (e: any) {
    console.error('\nOne or more tasks failed:', e.message);
  }
}

runTasks();

view raw JSON →