TL;DR
chalk is the most popular terminal coloring library — chainable API (chalk.red.bold("text")), automatic color level detection (256 colors, Truecolor), and an ecosystem of utilities built around it. kleur is the lightweight alternative — 2.5KB vs chalk's 15KB, same chaining API, no color detection (just uses whatever the terminal supports). colorette is the smallest — 1.5KB, function-based (no chaining), and faster for simple use cases. All three work with Node.js and modern terminals. For CLIs with complex styling, use chalk. For lightweight scripts and build tools, use kleur or colorette.
Key Takeaways
- chalk: ~200M weekly downloads — most popular, auto color detection, 256/Truecolor,
chalk.level - kleur: ~50M weekly downloads — 2.5KB, same chain API, lighter alternative
- colorette: ~60M weekly downloads — 1.5KB, function-based, fastest, used by Vite/Rollup
- chalk v5 is ESM-only — use
chalk@4for CommonJS orkleur/colorettefor CJS + ESM - None are needed in Node.js 22+ which has
util.styleText()for simple cases - colorette is preferred in build tools (Vite uses it) — small footprint matters there
Download Trends
| Package | Weekly Downloads | Bundle Size | ESM | CJS | Chain API |
|---|---|---|---|---|---|
chalk | ~200M | ~15KB | ✅ v5 | ✅ v4 | ✅ |
kleur | ~50M | ~2.5KB | ✅ | ✅ | ✅ |
colorette | ~60M | ~1.5KB | ✅ | ✅ | ❌ Functions |
chalk
chalk — the terminal string styling standard:
Basic usage
import chalk from "chalk"
// Basic colors:
console.log(chalk.red("Error: Package not found"))
console.log(chalk.green("Success: Package published"))
console.log(chalk.yellow("Warning: Deprecated package"))
console.log(chalk.blue("Info: Fetching npm data"))
console.log(chalk.gray("Debug: request to registry"))
// Background colors:
console.log(chalk.bgRed.white(" FAIL "))
console.log(chalk.bgGreen.black(" PASS "))
// Modifiers:
console.log(chalk.bold("Important"))
console.log(chalk.dim("Secondary info"))
console.log(chalk.italic("Emphasis"))
console.log(chalk.underline("Underlined"))
console.log(chalk.strikethrough("Deprecated API"))
Chaining
import chalk from "chalk"
// Chain styles:
console.log(chalk.red.bold("Error"))
console.log(chalk.blue.underline("Link"))
console.log(chalk.bgYellow.black.bold(" WARNING "))
// Template literals:
const pkg = "react"
const score = 95
console.log(chalk.cyan(`Package: ${pkg}`) + " " + chalk.green(`Score: ${score}/100`))
// Nested:
console.log(chalk.blue(`Blue ${chalk.red("Red")} Blue again`))
256 colors and Truecolor
import chalk from "chalk"
// 256-color palette (0-255):
console.log(chalk.ansi256(196)("Red-ish"))
console.log(chalk.bgAnsi256(21)("Blue background"))
// Truecolor (RGB):
console.log(chalk.rgb(255, 136, 0)("PkgPulse orange"))
console.log(chalk.hex("#FF8800")("PkgPulse orange (hex)"))
console.log(chalk.bgHex("#FF8800").black("Orange background"))
// HSL:
console.log(chalk.hsl(30, 100, 50)("Warm orange"))
Color level detection
import chalk, { Chalk } from "chalk"
// chalk.level:
// 0 = no colors (e.g., NO_COLOR env var, non-TTY)
// 1 = basic 16 colors
// 2 = 256 colors
// 3 = Truecolor (16M colors)
console.log(`Terminal color level: ${chalk.level}`)
// Force a specific level (useful for testing):
const chalkForced = new Chalk({ level: 1 })
// Respect NO_COLOR environment variable:
// chalk does this automatically — if process.env.NO_COLOR is set, chalk.level = 0
CLI output helpers
import chalk from "chalk"
// Status indicators:
const success = chalk.green("✓")
const failure = chalk.red("✗")
const warning = chalk.yellow("⚠")
const info = chalk.blue("ℹ")
console.log(`${success} Tests passed`)
console.log(`${failure} Build failed`)
console.log(`${warning} 3 deprecation warnings`)
console.log(`${info} Fetching package data...`)
// Progress output:
function printPackageHealth(name: string, score: number) {
const color = score >= 80 ? chalk.green : score >= 60 ? chalk.yellow : chalk.red
const bar = "█".repeat(Math.floor(score / 10)) + "░".repeat(10 - Math.floor(score / 10))
console.log(
chalk.bold(name.padEnd(30)) +
color(bar) +
" " +
color.bold(`${score}/100`)
)
}
printPackageHealth("react", 95)
printPackageHealth("lodash", 72)
printPackageHealth("moment", 45)
CommonJS note (chalk v4)
// chalk v5 is ESM-only — use v4 for CommonJS:
// package.json: "chalk": "^4"
const chalk = require("chalk")
// Or use kleur/colorette which support both
kleur
kleur — 2.5KB alternative with the same chain API:
Basic usage
import kleur from "kleur"
// Same API as chalk:
console.log(kleur.red("Error"))
console.log(kleur.green("Success"))
console.log(kleur.yellow().bold("Warning"))
// Chaining (slightly different — use function call for chaining):
console.log(kleur.red().bold("Red and bold"))
console.log(kleur.bgBlue().white().bold(" INFO "))
// Colors:
console.log(kleur.black("black"))
console.log(kleur.red("red"))
console.log(kleur.green("green"))
console.log(kleur.yellow("yellow"))
console.log(kleur.blue("blue"))
console.log(kleur.magenta("magenta"))
console.log(kleur.cyan("cyan"))
console.log(kleur.white("white"))
console.log(kleur.gray("gray"))
Template literal tag
import { red, green, bold, cyan } from "kleur"
// Named exports:
console.log(red("Error"))
console.log(green("Success"))
console.log(bold("Important"))
console.log(cyan("Info"))
// Compose:
const pkg = "react"
console.log(`${bold(pkg)}: ${green("healthy")} (score: ${cyan("95/100")})`)
NO_COLOR support
import kleur from "kleur"
// kleur respects NO_COLOR and FORCE_COLOR env vars:
// Set FORCE_COLOR=0 or NO_COLOR=1 to disable
// Check if colors are enabled:
kleur.enabled // boolean
// Disable programmatically:
kleur.enabled = false
console.log(kleur.red("This prints without color now"))
kleur.enabled = true
colorette
colorette — 1.5KB, function-based, used by Vite and Rollup:
Basic usage
import { red, green, yellow, blue, bold, dim, reset } from "colorette"
// Function-based — no chaining:
console.log(red("Error: Package not found"))
console.log(green("Success: Data fetched"))
console.log(yellow("Warning: Rate limit approaching"))
console.log(blue("Info: Connecting to registry"))
console.log(bold("Important notice"))
console.log(dim("Secondary information"))
Compose manually
import { red, bold, bgBlue, white, dim } from "colorette"
// No chaining — compose by nesting:
console.log(bold(red("Error"))) // Red and bold
console.log(bgBlue(white("Info"))) // Blue bg, white text
console.log(bold(red("✗")) + " " + "Build failed") // Mixed
// In practice, define helpers:
const error = (msg: string) => bold(red(`✗ ${msg}`))
const success = (msg: string) => bold(green(`✓ ${msg}`))
const warn = (msg: string) => bold(yellow(`⚠ ${msg}`))
const info = (msg: string) => blue(`ℹ ${msg}`)
console.log(error("Build failed"))
console.log(success("Tests passed"))
console.log(warn("3 deprecation warnings"))
console.log(info("Fetching data..."))
Checking color support
import { isColorSupported } from "colorette"
if (isColorSupported) {
console.log("Terminal supports colors")
} else {
console.log("No color support")
}
// colorette also respects:
// NO_COLOR env var — disables all colors
// FORCE_COLOR env var — forces colors even in non-TTY
Why Vite uses colorette
// colorette is used in Vite's build output:
// - 1.5KB — matters in build tools where deps add up
// - No auto-detection overhead — build tools know their context
// - ESM + CJS — works in both module systems
// - Functions, not chain — simpler tree-shaking
// The Vite output you see:
// ✓ 423 modules transformed.
// dist/index.html 0.41 kB
// dist/assets/index-abc.js 142.30 kB │ gzip: 46.80 kB
Feature Comparison
| Feature | chalk | kleur | colorette |
|---|---|---|---|
| Bundle size | ~15KB | ~2.5KB | ~1.5KB |
| Chain API | ✅ | ✅ | ❌ Functions |
| ESM | ✅ v5 | ✅ | ✅ |
| CJS | ✅ v4 | ✅ | ✅ |
| 256 colors | ✅ | ❌ | ❌ |
| Truecolor (RGB) | ✅ | ❌ | ❌ |
| Hex colors | ✅ | ❌ | ❌ |
| Color detection | ✅ Auto | ⚠️ Manual | ✅ isColorSupported |
| NO_COLOR | ✅ | ✅ | ✅ |
| FORCE_COLOR | ✅ | ✅ | ✅ |
| Template literal tag | ✅ | ❌ | ❌ |
| TypeScript | ✅ | ✅ | ✅ |
When to Use Each
Choose chalk if:
- Building a CLI tool where rich styling matters (256 colors, Truecolor, hex)
- You need automatic color level detection (chalk handles TTY, pipes, CI/CD)
- Using template literal tags for complex styled output
- Bundle size isn't a constraint
Choose kleur if:
- You want a lighter chalk alternative with the same chain API
- CommonJS + ESM compatibility in one package
- Build tools or scripts where 12KB of savings matters
- Basic 16 colors are sufficient (no need for Truecolor)
Choose colorette if:
- Bundle size is critical (Vite, Rollup, other build tools)
- You prefer function composition over chaining
- ESM and CJS support with minimal overhead
- Simple coloring — no complex multi-color chains
Consider Node.js util.styleText() if:
- Node.js 22+ and no third-party dep is preferred
- Only need basic styling (bold, red, green, etc.)
util.styleText(["red", "bold"], "Error")— built-in, no install needed
Migration Guide
From chalk v4 to chalk v5 (ESM migration)
Chalk v5 is ESM-only, which breaks CommonJS projects. Two paths forward:
// Option 1: Stay on chalk v4 (CommonJS compatible)
// package.json: "chalk": "^4"
const chalk = require("chalk")
console.log(chalk.red("Error"))
// Option 2: Migrate to ESM (chalk v5)
// package.json: "type": "module"
import chalk from "chalk"
console.log(chalk.red("Error"))
// Option 3: Switch to kleur (supports both CJS and ESM)
// package.json: "kleur": "latest"
import kleur from "kleur" // ESM
// OR: const kleur = require("kleur") // CJS — same package
console.log(kleur.red("Error"))
For CJS projects that can't migrate to ESM, kleur is the most direct chalk replacement. Its chain API (kleur.red().bold()) is nearly identical to chalk's (chalk.red.bold), with the difference that kleur uses function calls for chaining rather than property access.
Community Adoption in 2026
chalk leads by a wide margin at approximately 120 million weekly downloads — one of the most downloaded npm packages overall. This reflects its presence as a dependency in virtually every popular Node.js CLI tool and build system. ESLint, Prettier, Webpack, Jest, and hundreds of other tools depend on chalk directly. Even if you don't install chalk explicitly, it is present in almost every Node.js project's node_modules.
kleur and colorette each see several million weekly downloads, driven primarily by build tool authors who adopt them for their minimal footprint. colorette is used internally by Vite, Rollup, and esbuild's output formatting. kleur is used by tools like uvu (the test runner) and various lightweight CLI utilities from the lukeed ecosystem. For application developers choosing a coloring library directly, the choice between the three comes down to whether you need chalk's full feature set (Truecolor, template tags) or whether basic 16-color support is sufficient.
CI Environment Detection and Color Output Strategy
Terminal color libraries must detect whether their output will be read by a human or a machine and behave appropriately in both contexts.
Color level detection is the core mechanism. The ANSI color standards define four levels: 0 (no color, for pipes and CI), 1 (basic 16 colors), 2 (256-color ANSI), and 3 (16 million color RGB/TrueColor). Chalk detects the level automatically using has-flag, checking FORCE_COLOR, NO_COLOR, TERM, COLORTERM, and TERM_PROGRAM environment variables. A Chalk instance's detected level is exposed as chalk.level, which you can read to conditionally render color-dependent output.
The NO_COLOR standard (no-color.org) has been widely adopted since 2018: when NO_COLOR is set in the environment (to any value), programs must not output ANSI color escape codes. All three libraries respect this standard. This matters for: redirecting terminal output to files (node script.js > output.txt), capturing output in test assertions, piping through programs that don't support ANSI codes, and accessibility tools that read terminal output.
GitHub Actions injects FORCE_COLOR=1 by default in GitHub-hosted runners, enabling color output in CI logs. This produces readable, color-coded build output in the GitHub Actions log viewer. Kleur and colorette both respect FORCE_COLOR, so they produce colored output in GitHub Actions automatically. If you notice missing colors in other CI environments (GitLab CI, Bitbucket Pipelines), check whether the runner sets FORCE_COLOR or whether you need to set it manually in your CI configuration.
Testing color output requires care: tests that assert on console output content should strip ANSI escape codes before comparison. The strip-ansi package (a Chalk dependency) provides stripAnsi(string) for this purpose. A common mistake is to write tests that pass locally (with color disabled in the test environment) but fail in some CI environments where color is enabled by FORCE_COLOR, causing escape codes to appear in the asserted output string.
For library authors, consider making color output optional via a configuration option and respect the caller's choice. A library that forces color output (or suppresses it) regardless of environment reduces the composability of CLI tools built on top of it.
Performance Benchmarks for High-Volume CLI Output
For CLIs that produce thousands of styled output lines — log processors, build progress reporters, real-time monitoring tools — the performance difference between chalk, kleur, and colorette becomes relevant. colorette's function-based API has less overhead than chalk's Proxy-based chain API because it avoids the dynamic property lookup that chalk uses to build style chains. Kleur's chain API uses a similar Proxy mechanism to chalk for its fluent calls. In benchmarks measuring styled string generation throughput, colorette consistently leads by 15-30% over chalk, with kleur in between. For most CLI tools that produce human-readable output at interactive speeds (tens of lines per second), this difference is imperceptible. It becomes meaningful only when you're generating machine-scale output — millions of styled log lines, real-time terminal dashboards with frequent redraws, or CLI tools that run in tight loops processing streaming data. In those scenarios, pre-computing style strings outside the hot path (creating const errorStyle = red and reusing it) provides more benefit than switching libraries.
Node.js Built-in Alternative and When to Skip Dependencies
Node.js 22 introduced util.styleText(format, text) as a built-in alternative to third-party terminal color libraries. It supports the same basic modifiers — "red", "bold", "underline", "bgBlue", and combinations via array syntax like util.styleText(["red", "bold"], "Error"). For simple scripts, server-side logging, or CLI tools that target Node.js 22+ exclusively, util.styleText eliminates the need for any dependency. It respects the NO_COLOR environment variable and TTY detection automatically. The tradeoff is that it only supports basic 16-color ANSI formatting — no 256-color palette, no RGB/hex, and no template tag syntax. For any project that needs chalk's full feature set (Truecolor, hex colors, chaining complex styles), chalk or kleur remain necessary. But for the common case of color-coded log output in a Node.js 22+ server or utility script, the built-in is sufficient and reduces your dependency surface.
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on chalk v5.x, kleur v4.x, and colorette v2.x.
Compare developer tool and CLI packages on PkgPulse →
See also: Chalk vs picocolors and cac vs meow vs arg 2026, archiver vs adm-zip vs JSZip (2026).