CLI tool
sindresorhus/yoctocolors avatar
sindresorhus/yoctocolors

yoctocolors: a zero-dependency ANSI color package for Node CLIs

The smallest and fastest command-line coloring package on the internet

878 stars36 forksJavaScriptMIT

At a glance

What is it?
yoctocolors is Sindre Sorhus's tiny terminal coloring library, shipped as ESM with a separate CommonJS package, and it is deliberately narrower than chalk. It wins on install weight and tree shaking, and loses on style coverage you actually use in a terminal.
Who is it for?
Adopt yoctocolors when your CLI only needs a handful of basic colors and you care about install weight and tree shaking. Do not adopt it when you need chalk's broader style handling or a stable template API.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 35 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A coloring library that stops at ANSI escapes

yoctocolors does one thing: it wraps strings in ANSI escape sequences so that command line output carries color. The repository description calls it the smallest and fastest command-line coloring package on the internet, and the manifest backs the size claim structurally rather than with prose. The published files array in package.json lists exactly four entries, index.js, index.d.ts, base.js and base.d.ts, and the dependencies section is empty. There is no transitive tree to audit, no transitive tree to dedupe when several packages depend on it, and nothing to audit after install.

The package targets Node directly. The manifest declares type module and an engines field requiring node >=18, which is why the package documentation splits installation into an ESM path and a CommonJS path rather than shipping one dual-mode entry point. The color names themselves are conventional terminal names: black through white, the bright variants, gray, and matching bg-prefixed background names.

That narrowness is the design, not a limitation discovered later. The maintainer states the intent plainly in the package documentation, pointing readers to chalk when they want a more established option. For an engineer choosing a color helper for a small CLI, that is a fair trade: you get colors, you get nothing else, and the size of the thing in your lockfile reflects that.

Tree shaking works because the manifest says so

Tree shaking is not a free property of ESM packages. It depends on how the module is written and on whether the manifest marks it as having side effects. yoctocolors handles both. package.json sets sideEffects to false, and the exports map points types at index.d.ts and default at index.js. Bundlers can therefore drop the helpers a given module never calls.

The package documentation shows two equivalent call styles. A default import gives you a namespace object:

js
import colors from 'yoctocolors';

console.log(colors.red('Yo!'));

while named imports give you individual functions:

js
import {red, blue, green} from 'yoctocolors';

console.log(red('Yo!'));

console.log(blue(`Welcome to the ${green('yoctocolors')} package!`));

The named form is the one that pays off under bundling, since a tree shaker can remove unused color functions from the final bundle. The manifest ships a separate index.d.ts and a base.d.ts alongside the runtime files, so TypeScript consumers get types without pulling extra JavaScript.

There is a second file, base.js, in the published set, and the package documentation does not explain its role separately. Based on the repository layout it appears to hold the shared implementation the exported wrappers build on, and it ships because the exported functions reference it.

Color detection follows Node rather than guessing

Terminals vary, pipes do not, and CI logs are usually neither. yoctocolors delegates that decision to Node's own stream capability check, described in the package documentation as basic color detection. The library then offers three environment overrides. Setting FORCE_COLOR to 1 forces colors on. Setting NO_COLOR or NODE_DISABLE_COLORS to any value forces them off.

The FORCE_COLOR path matters for testing. The ava configuration inside package.json sets FORCE_COLOR to 1 for the test run, which is how the test suite asserts on styled output without depending on the terminal the developer happens to be using. A library that colored unconditionally would pass those assertions and then break downstream in a pipe, so the override behavior is the more important half of this design.

Note the asymmetry in the knobs. FORCE_COLOR takes a meaningful value, while the two disabling variables are documented as accepting any value. If you script around this, treat NO_COLOR as a boolean flag rather than something you set to 0 expecting it to stay disabled, since any value at all turns it off.

Style coverage runs ahead of what terminals render

Start with the ESM package if your project already imports modules:

sh
npm install yoctocolors

The default export gives you a namespace of helpers:

js
import colors from 'yoctocolors';

console.log(colors.red('Yo!'));

console.log(colors.blue(`Welcome to the ${colors.green('yoctocolors')} package!`));

The package documentation lists thirteen modifiers, from reset and bold through dim, hidden, inverse, overline and strikethrough, plus five underline variants that cover solid, double, curly, dotted and dashed lines. Then it annotates most of the decorative ones as not widely supported: italic, every underline variant, overline and strikethrough all carry that note. Only reset, bold, dim, inverse and hidden are presented without a caveat.

That split is the practical catch. The API surface is larger than the set of styles a typical terminal will show correctly, and a developer who reaches for underlineCurly because the name is there will get a plain underline or no underline depending on the emulator. The package documentation handles this explicitly with a worked example, showing underlineRed applied to underlineCurly producing a red squiggle below unstyled text.

Underline colors are also a separate axis from the text color. There are sixteen underline color names, and the package documentation notes the underline color is set independently of the text color, so it is only visible when an underline style is applied at the same time. Set underlineRed on its own and nothing shows. This is the kind of detail that is easy to get wrong once and never again.

The benchmark table settles little

The package documentation includes output from a bundled benchmark.js run using the @jonahsnider/benchmark package listed in devDependencies. yoctocolors, colorette and picocolors each report 8,000,000 ops/sec. nanocolors reports 5,988,024, chalk and kleur/colors both report 4,807,692, kleur reports 4,784,689, ansi-colors reports 2,178,649 and cli-color reports 585,138.

Read that table carefully and it does not separate the leaders. Three libraries sit on the same reported figure, so the benchmark cannot tell you that yoctocolors is faster than colorette or picocolors. It does show a real gap to the bottom of the field, where cli-color runs roughly an order of magnitude slower, and a visible gap to chalk. The comparison is meaningful in the sense that the library is not slower than its rivals. It is not meaningful as a reason to prefer it over colorette or picocolors.

The benchmark also runs with color output forced on, consistent with the ava environment configuration. Every microsecond spent building escape sequences is measured, which is the right shape for this comparison, but it measures string construction rather than the cost of writing to a terminal.

CommonJS callers install a different package

The install section gives two commands. The ESM path installs yoctocolors:

sh
npm install yoctocolors

and the CommonJS path installs a different package:

sh
npm install yoctocolors-cjs

The manifest for the main package declares type module and a single default export, so a require call against it does not work as written. The CommonJS users are pointed to a separately published package name instead of a second export condition inside the same package.

This is worth knowing before you start, because the split is invisible until the runtime throws. The package documentation does not document the version relationship between yoctocolors and yoctocolors-cjs, nor whether the two are released in lockstep, so a team running both an ESM service and a CommonJS build script ends up with two dependencies to keep aligned. Nothing in the repository layout suggests they are generated from one another, so treat them as separate installs.

For a project on Node >=18 that already uses ESM, none of this applies. The plain install is all you need.

Release history and the state of the repository

Three releases are listed. v2.2.0 shipped on 2026-07-26, v2.1.2 on 2025-08-19 and v2.1.1 on 2024-07-03. The repository is not archived, and the last push was on 2026-09-18, so the code has been touched within the current month even though the version number has moved only twice since 2024.

The license is MIT, so the package can be vendored, modified and redistributed inside a closed product without a separate commercial arrangement. The package documentation points to a funding link for the author and lists yoctodelay and chalk as related projects.

Development tooling is visible in the manifest. The test script runs xo for linting, ava for the test run and tsd for type definition tests, with index.test-d.ts sitting at the top level. fixture.js and example.js are present in the repository root but are not part of the published files list, so they exist for development and examples rather than for consumers.

Editorial conclusion

Adopt yoctocolors when your CLI only needs a handful of basic colors and you care about install weight and tree shaking. Do not adopt it when you need chalk's broader style handling or a stable template API. Before switching, check the modifiers your output relies on against the list the package documentation marks as not widely supported, and confirm the ESM versus CommonJS entry point matches your runtime.

Frequently asked questions

How do I install yoctocolors in a Node project?

Run npm install yoctocolors for an ESM project, or npm install yoctocolors-cjs if you need CommonJS. The main package declares type module and requires node >=18, so the CommonJS path is a separate published package rather than a second export condition.

How do I force yoctocolors to output colors in a CI log?

Set the FORCE_COLOR environment variable to 1 to enable colors regardless of terminal detection. The ava configuration in package.json sets FORCE_COLOR to 1 for the test run in the same way.

Which yoctocolors styles are not widely supported by terminals?

The package documentation marks italic, the underline variants, overline and strikethrough as not widely supported. reset, bold, dim, inverse and hidden are listed without that caveat.

How does yoctocolors compare with chalk?

The package documentation points you to chalk when you want a more established option. In the bundled benchmark, yoctocolors reports 8,000,000 ops/sec against 4,807,692 for chalk, though colorette and picocolors report the same figure as yoctocolors, so the numbers do not separate the top three.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. sindresorhus/yoctocolors on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sindresorhus-yoctocolors.svg)](https://hysenlabs.com/projects/sindresorhus-yoctocolors)

Was this useful?