mrkeyoor.com_
Wed 05 Aug 19:53 UTC
npmCLI & Toolingupdated 05 Aug 2026

chalk

Chalk styles terminal output. You chain style names and call the result as a function: chalk.blue.bold('hi') wraps the text in the right ANSI escape codes. It detects how much color the terminal supports (none, 16, 256, or 16 million), downsamples rgb and hex colors to what is available, respects FORCE_COLOR and --no-color conventions, and nests styles correctly, which naive string concatenation of escape codes gets wrong. Zero dependencies, and used as the styling layer by a huge share of Node CLI tools.

Verdict

Still the default way to color terminal output, with the best API and the most correct edge-case handling; the smaller alternatives exist mostly because chalk 5 dropped CommonJS. If you can use ESM, use chalk; if you are stuck on CJS or truly size-obsessed, picocolors is fine.

API stability4/5The chained API has been unchanged for a decade; the majors that hurt were about packaging (v5 ESM-only) and Node floors (v6 needs 22+), not the API itself.
Docs4/5The README documents every style, color model, and detection rule with examples; there is no separate site, and advanced detection behavior lives partly in the supports-color README.
Maintenance5/5Pushed July 2026 with a clean tracker (0 open issues and PRs) and long-term maintainers Sindre Sorhus and Josh Junon; v6 shipped in 2026.
Ecosystem5/5Roughly 115,000 packages depend on it per the README, and the chalk org maintains the companion pieces (supports-color, strip-ansi, ansi-styles) most terminal tooling is built from.

Use it if

  • You are building a Node CLI and want readable, composable styling (chalk.red.bold, nesting, template literals) instead of raw escape codes
  • You need color support detection handled for you, including CI environments, FORCE_COLOR, --color/--no-color flags, and separate stdout/stderr detection
  • You use rgb/hex brand colors and want automatic downsampling on terminals that only do 16 or 256 colors
  • It is almost certainly already in your dependency tree, so using it adds nothing to install size
Skip it if

Setup reality

npm install chalk, import chalk from 'chalk', done: zero dependencies and types are bundled. The one real hurdle is module format: since v5 the package is pure ESM, so CommonJS projects and some Jest or older TypeScript setups fail with ERR_REQUIRE_ESM and either stay on chalk 4 or move their build to ESM. Also note that color detection means output differs by environment: piped output and CI logs get no color unless you set FORCE_COLOR, which regularly surprises people when their snapshot tests or logs look different locally versus in CI.

Patterns

Color a stringbasic-color

import chalk from 'chalk';

console.log(chalk.blue('Hello world!'));
console.log(chalk.green('ok') + ' ' + chalk.red('fail'));

Pure ESM since v5: require('chalk') throws ERR_REQUIRE_ESM in CommonJS. Stay on chalk@4 or convert the project to ESM.

Combine and nest styleschain-styles

import chalk from 'chalk';

console.log(chalk.blue.bgRed.bold('Hello world!'));
console.log(chalk.red('error', chalk.underline.bgBlue('code') + '!'));
console.log(chalk.green('outer ' + chalk.blue.bold('inner') + ' outer again'));

Order does not matter and later styles win conflicts, so chalk.red.yellow.green is just chalk.green. Nesting restores the outer style correctly after the inner one ends.

Style values inside template literalstemplate-literals

import chalk from 'chalk';

console.log(`
CPU: ${chalk.red('90%')}
RAM: ${chalk.green('40%')}
DISK: ${chalk.yellow('70%')}
`);

Plain template literals are the idiomatic pattern; tagged-template syntax lives in the separate chalk-template package.

Define reusable theme helpersdefine-theme

import chalk from 'chalk';

const error = chalk.bold.red;
const warning = chalk.hex('#FFA500');

console.log(error('Error!'));
console.log(warning('Warning!'));

A partially applied chain is just a function you can export; this keeps color choices in one module instead of sprinkled through the codebase.

Use truecolor rgb and hex valuesrgb-hex-colors

import chalk from 'chalk';

console.log(chalk.rgb(123, 45, 67).underline('reddish'));
console.log(chalk.hex('#DEADED').bold('bold gray'));
console.log(chalk.bgHex('#FF8800')('orange background'));

On terminals without truecolor these are downsampled automatically, so #FF0000 becomes bright red at level 1 rather than failing.

Create an instance with a fixed color levelforce-color-level

import { Chalk } from 'chalk';

const noColor = new Chalk({ level: 0 });
const truecolor = new Chalk({ level: 3 });

console.log(noColor.red('plain text'));

Prefer a new Chalk instance over assigning chalk.level, which mutates a shared singleton for every consumer in the process. Levels outside 0-3 throw.

Style stderr with its own detectionstderr-output

import { chalkStderr } from 'chalk';

console.error(chalkStderr.red('failed to connect'));

stdout and stderr can have different color support (one piped, one a TTY); chalkStderr detects against stderr so redirected output stays clean.

Check what the terminal supportsdetect-color-support

import chalk, { supportsColor } from 'chalk';

if (supportsColor) {
  console.log('level', chalk.level); // 1, 2, or 3
}
if (supportsColor && supportsColor.has16m) {
  console.log('truecolor available');
}

Users can override detection with FORCE_COLOR=0..3 or --color/--no-color; your code should trust the detected level rather than re-checking isTTY.

Get color in CI and piped outputci-force-color

# color is disabled when output is piped or in most CI
FORCE_COLOR=3 node cli.js | less -R

# explicitly disable for clean logs
FORCE_COLOR=0 node cli.js > build.log

This is the top chalk confusion: no code change makes piped output colored, only the environment variable or --color flag, by design.

Validate user-supplied style namesvalidate-style-names

import chalk, { modifierNames, foregroundColorNames } from 'chalk';

function paint(styleName, text) {
  if (!foregroundColorNames.includes(styleName)) {
    throw new Error(`unknown color: ${styleName}`);
  }
  return chalk[styleName](text);
}

The exported name arrays (modifierNames, foregroundColorNames, backgroundColorNames, colorNames) exist exactly for wrappers that accept style strings from config.

Color the underline separately from the textunderline-colors

import chalk from 'chalk';

console.log(chalk.underlineRed.underlineCurly('typo'));
console.log(chalk.underlineRgb(15, 100, 204).underline('linked'));

New in chalk 6, and only visible when an underline modifier is also applied; terminal support for curly or colored underlines is still spotty.

Alternatives

PackageRegistryPick it when
picocolorsnpmYou want the smallest practical color package and only need the standard 16 colors, from ESM or CJS.
ansisnpmYou want a chalk-compatible chained API plus truecolor in a smaller dual ESM/CJS package.
kleurnpmYou want a tiny no-dependency API of chained method calls and can skip rgb/hex support.