Highly opinionated command line UI KIT

Downloads in past


4403.0.26 months ago2 years agoMinified + gzip package size for @poppinss/cliui in KB


Command line UI Kit used by AdonisJS

This repo is a command line UI Kit used by the AdonisJS framework to design its command line interfaces.
The kit is highly opinionated and we will not allow configurable settings in the near future. We want to be consistent with our UI's without worrying about the configuration.
!gh-workflow-imagegh-workflow-url !typescript-imagetypescript-url !npm-imagenpm-url !license-imagelicense-url !synk-imagesynk-url

Table of contents

- [`success(message, prefix?, suffix?)`](#successmessage-prefix-suffix)
- error(message, prefix?, suffix?) - fatal(message, prefix?, suffix?) - warning(message, prefix?, suffix?) - info(message, prefix?, suffix?) - debug(message, prefix?, suffix?) - log(message) - logError(message) - logUpdate(message) - action.succeeded(message) - action.‌skipped(message) - action.failed(message, errorMessage) - Task Renderers - Running tasks - Verbose renderer


Install the package from the npm registry by running following command.
npm i @poppinss/cliui

# Yarn users
yarn add @poppinss/cliui


Import the components you want to use from the package.
import { logger, instructions, sticker, tasks, table } from '@poppinss/cliui'

logger.info('hello world')

const spinner = logger.await('downloading')

await someTimeConsumingTask()


The logger exposes the following methods.

success(message, prefix?, suffix?)

Log success message. The message is printed to stdout.
logger.success('Account created')

// [ success ]  Account created

Optional prefix
logger.success('Account created', 'ap-south-1')

// [ap-south-1] [ success ]  Account created

Optional suffix
logger.success('Account created', undefined, 'ap-south-1')

// [ success ]  Account created (ap-south-1)

The prefix and suffix are support on all logger methods except logger.action

error(message, prefix?, suffix?)

Log an error message. The message is printed to stderr.
logger.error('Unable to write. Disk full')

// Or log error object
logger.error(new Error('Unable to write. Disk full'))

// [ error ]  Unable to write. Disk full

fatal(message, prefix?, suffix?)

The logger.error does not print the error stack. You must use logger.fatal to print the error stack.
logger.fatal(new Error('Unable to write. Disk full'))

warning(message, prefix?, suffix?)

Print a warning message. Message is written to stdout.
logger.warning('Running out of disk space')

// [ warn ]  Running out of disk space

info(message, prefix?, suffix?)

Print an info message. Message is again written to stdout.
logger.info('Your account is has been updated')

// [ info ]  Your account is has been updated

debug(message, prefix?, suffix?)

Print a debug message. Message is printed to stdout.
logger.debug('Something just happened')

// [ debug ]  Something just happened


Similar to console.log, but instead uses the Logger renderer.
We will talk about renderers later in this document, since they make testing of log message little bit easier.

logger.log('hello world')


Similar to console.error, but instead use the Logger renderer.
log.logError('this is an error message')


Log a message that overwrites the previously logged message. The method is helpful for building progress bars or animations.
logger.logUpdate(`downloading ${i}%`)

// Once completed, persist the message on console

Here is a complete example of showing the downloading progress.
const sleep = () => new Promise((resolve) => setTimeout(resolve, 50))

async function run() {
  for (let i = 0; i <= 100; i = i + 2) {
    await sleep()
    logger.logUpdate(`downloading ${i}%`)




In order to log results of an action/task, we make use of the action method.
const action = logger.action('create')

An action can end in one of the following states.


Action completed successfully
const action = logger.action('create')


Skipped action
const action = logger.action('create')

action.failed(message, errorMessage)

Action failed, an error message is required to share more context
const action = logger.action('create')
action.failed('server.ts', 'File already exists')


Instructions are mainly the steps we want someone to perform in order to achieve something. For example:
  • Display instructions to start the development
  • Or display instructions to bundle the code for production

import { instructions, logger } from '@poppinss/cliui'

  .add(`cd ${logger.colors.cyan('hello-world')}`)
  .add(`Run ${logger.colors.cyan('node ace serve --watch')} to start the server`)

  • Calling the instructions() begins a new instructions block
  • Next, you can add new lines by using the .add() method.
  • Finally, call the render() method to render it on the console.


Similar to the instructions, but a sticker does not prefix the lines with a pointer > arrow. Rest is all same.
It is helpful for displaying a message that needs the most attention. For example:
  • Update the CLI version
  • Or, the address to access the local server

import { sticker, logger } from '@poppinss/cliui'

  .add('Started HTTP server')
  .add(`Local address:    ${logger.colors.cyan('http://localhost:3333')}`)
  .add(`Network address:  ${logger.colors.cyan('http://localhost:3333')}`)


We make use of tasks when performing multiple actions in respond to a command. For example:
  • Create a new AdonisJS app
  • Or, Setup packages after installation

The UI for the tasks is designed to only handle tasks running in sequence.

Task Renderers

Task has two renderers minimal and verbose. The minimal renderer is the default choice and switch to verbose in one of the following situations.
  • Command line is not interactive (no tty)
  • Or someone has explicitly opted for verbose output.

Running tasks

Following is a very simple example of creating and running multiple tasks.
import { tasks } from '@poppinss/cliui'

await tasks()
  .add('clone repo', async (logger, task) => {
    logger.info(`cloning ${someRepoUrl}`)

    await performClone()
    await task.complete()
  .add('install dependencies', async (logger, task) => {
    const spinner = logger.await('running npm install')

    await performInstall()

    await task.complete()

  • The add method accepts the task title and the callback function to invoke in order to perform the task
  • Once, you are done with the task jobs, you must call await task.complete() to complete the task. The await is important here.
  • In order to mark task as failed, you can call the task.fail method. All upcoming tasks will be stopped in case of a failure.
ts await task.fail(new Error('Network error'))
By default, the minimal renderer is used and pivots to the verbose renderer only when terminal is not interactive.

Verbose renderer

In order to run tasks explicitly in the verbose mode, you can create the tasks instance using tasks.verbose() method.