ANSI escape codes in JavaScript / Node.js
Node's `process.stdout.write` and `console.log` are both byte-clean. The browser console (DevTools) accepts a CSS-styled subset of escapes via `%c`, NOT ANSI bytes — those only render in real terminals. For ergonomics, `chalk` is the canonical choice; `picocolors` is the same idea in ~100x less code.
Recommended libraries
- chalk
Chainable colour API: `chalk.bold.red('oops')`. Auto-detects support level (16 / 256 / 16M colours), honours `NO_COLOR` and `FORCE_COLOR`.
- picocolors
Drop-in chalk-shaped API in ~100 LOC and zero dependencies. Used by Vite, Vue, Tailwind for cold-start time.
- ansi-escapes
Constants and helpers for non-SGR escapes: cursor moves, clear lines, alt screen, OSC 8 hyperlinks, OSC 52 clipboard, iTerm2 imgcat.
- ink
React for the terminal — components render to ANSI. Used by Gatsby, Yarn, Shopify CLI. Pairs with `ink-ui` for batteries-included widgets.
Idiomatic patterns
process.stdout.write('\x1b[1;31merror:\x1b[0m permission denied\n');import chalk from 'chalk';
console.log(chalk.bold.red('error:'), 'permission denied');
console.log(chalk.rgb(203, 166, 247)('lavender truecolor'));function link(text, url) {
const ESC = '\x1b';
return `${ESC}]8;;${url}${ESC}\\${text}${ESC}]8;;${ESC}\\`;
}
console.log('see ' + link('the docs', 'https://ansicode.eversources.app'));const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
let i = 0;
process.stdout.write('\x1b[?25l'); // hide cursor
const id = setInterval(() => {
process.stdout.write('\r' + frames[i = (i + 1) % frames.length] + ' loading');
}, 80);
process.on('exit', () => {
clearInterval(id);
process.stdout.write('\r\x1b[K\x1b[?25h'); // restore line + cursor
});