Skip to content

Subtasks

listr2 can be infinitely nested by utilizing subtasks, which is the core part of the design.

A Task can return a new Listr. But rather than calling it by invoking a new Listr to get the full auto-completion features depending on the parent task's selected renderer, it is mandatory to call it through the Task itself by task.newListr() since they are sharing components internally that makes the application tick.

Subtasks can be nested indefinitely as long as the terminal width is enough to support them.

Subtasks give the advantage of grouping similar tasks, changing the behavior of Listr for a certain set of tasks, or cleaning up the rendering area when certain tasks have finished.

Usage

ts
import { delay, Listr } from 'listr2'

const tasks = new Listr(
  [
    {
      title: 'This task will execute.',
      task: (ctx, task): Listr =>
        task.newListr([
          {
            title: 'This is a subtask.',
            task: async (): Promise<void> => {
              await delay(3000)
            }
          }
        ])
    }
  ],
  { concurrent: false }
)

await tasks.run()

Example

You can find the related examples here.

Overwriting the Default Behavior Through Subtask Options

You can change the behavior of Listr, and the selected renderer of the parent through the options of a subtask.

This includes selected renderer options as well as Listr options like exitOnError, concurrent to be set per-subtaskly independent of the parent task, while it will always inherit the defaults from the parent task.

Due to design limitations of making renderers a bit simpler, some of the options that cannot be changed from the renderer are marked as @global in the hover documentation. This is not disabled through typings due to keeping the renderer instances fully self-contained.

Code Example
ts
import { delay, Listr } from 'listr2'

const tasks = new Listr(
  [
    {
      title: 'This task will execute.',
      task: (ctx, task): Listr =>
        task.newListr(
          [
            {
              title: 'This is a subtask.',
              task: async (): Promise<void> => {
                await delay(3000)
              }
            },
            {
              title: 'This is an another subtask.',
              task: async (): Promise<void> => {
                await delay(2000)
              }
            }
          ],
          { concurrent: true, rendererOptions: { collapseSubtasks: true } }
        )
    },

    {
      title: 'This task will execute.',
      task: (ctx, task): Listr =>
        task.newListr(
          [
            {
              title: 'This is a subtask.',
              task: async (): Promise<void> => {
                await delay(3000)
              }
            },
            {
              title: 'This is an another subtask.',
              task: async (): Promise<void> => {
                await delay(2000)
              }
            }
          ],
          { concurrent: false, rendererOptions: { collapseSubtasks: false } }
        )
    }
  ],
  { concurrent: false }
)

const ctx = await tasks.run()

Access Parent Task from Subtasks

v2.6.0 #141

You can access the parent task class from subtasks by passing the function signature (parent) => Listr to task.newListr. This way you can change the title of the parent task or access its functionality.

Code Example
ts
import { delay, Listr } from 'listr2'

const tasks = new Listr(
  [
    {
      title: 'This task will execute.',
      task: (ctx, task): Listr =>
        task.newListr(
          (parent) => [
            {
              title: 'This is a subtask.',
              task: async (): Promise<void> => {
                await delay(3000)

                parent.title = 'I am changing the title from subtask.'
              }
            }
          ],
          { concurrent: true }
        )
    }
  ],
  { concurrent: false }
)

const ctx = await tasks.run()