Effortless, fully nestable ANSI styling — with both a tiny class-based core and a super-ergonomic Proxy interface.
import { ansi } from "ansi-escape-code/proxy";
console.log(ansi.red`Hello ${ansi.green.bold`Beautiful`} World`.toString());…or, if your environment does not support Proxy and/or template tag:
import { Ansi } from "ansi-escape-code";
console.log(
new Ansi(
{ foregroundColor: Ansi.STANDARD_RED },
"Hello ",
new Ansi(
{
foregroundColor: Ansi.STANDARD_GREEN,
weight: "bold",
},
"Beautiful"
),
" World"
).toString()
);- Why this library?
- Install
- Quick start
- TTY-aware entry points
- API Reference
AnsiclassAnsiOptionsAnsi.defaultOptions- Color constants & helpers
Ansi.tt(options): AnsiTemplateTagtoString(resolvedOuterOptions?: AnsiOptions): string
ansi(Proxy factory)AnsiFactorysurface
NoopAnsi
- Advanced: nesting & option resolution
- TypeScript types
- FAQ
- License
- True nesting: Inner styles override just what they change; outer styles are restored automatically.
- Zero surprises: Explicit
toString()— you choose when to emit escape codes. - Type-safe: Clear typings for colors, options, and template tags.
- Works everywhere: Use the ergonomic
ProxyAPI where supported, or fall back to the class-based API. - TTY-aware entry points: Automatically strip codes when writing to non-TTY streams (Node.js).
npm i ansi-escape-code
# if you use typescript
npm i -D @ansi-escape-code/typeimport { ansi } from "ansi-escape-code/proxy";
console.log(
ansi.red.bold`Error:`.toString(),
ansi.yellow` something went wrong`.toString()
);Chaining works exactly as you expect:
console.log(ansi.green.double_underline`important`.toString());Dynamic colors:
console.log(ansi.color(Ansi.TRUE_COLOR(255, 128, 0)).bold`🔥 Hot!`.toString());Use the low-level, fully typed Ansi class.
import { Ansi } from "ansi-escape-code";
const red = Ansi.tt({ foregroundColor: Ansi.STANDARD_RED });
const greenBold = Ansi.tt({
foregroundColor: Ansi.STANDARD_GREEN,
weight: "bold",
});
const out = red`Hello ${greenBold`Beautiful`} World`.toString();
console.log(out);Use the constructor directly when tagged templates aren’t an option:
import { Ansi } from "ansi-escape-code";
const out = new Ansi(
{ foregroundColor: Ansi.STANDARD_RED },
"Hello ",
new Ansi(
{ foregroundColor: Ansi.STANDARD_GREEN, weight: "bold" },
"Beautiful"
),
" World"
).toString();
console.log(out);This approach is fully compatible with all nesting and formatting behaviors — just more verbose than using tt() or the Proxy API.
Most CLIs should avoid emitting escape codes when the destination stream is not a TTY. Use these:
ansi-escape-code/nodeansi-escape-code/proxy-node
They behave like their counterparts but will return NoopAnsi wrappers (or similar behavior) when process.stdout.isTTY or process.stderr.isTTY is false.
export declare class Ansi {
readonly options: Partial<AnsiOptions>;
static readonly defaultOptions: AnsiOptions;
readonly parts: AnsiPart[];
constructor(options: Partial<AnsiOptions>, ...parts: AnsiPart[]);
toString(resolvedOuterOptions?: AnsiOptions): string;
static GRAYSCALE(n: number): AnsiColor;
static BASIC_RGB(r: number, g: number, b: number): AnsiColor;
static TRUE_COLOR(r: number, g: number, b: number): AnsiColor;
static tt(options: Partial<AnsiOptions>): AnsiTemplateTag;
// Standard color constants (see below)
static readonly STANDARD_BLACK: AnsiColor;
static readonly STANDARD_RED: AnsiColor;
...
static readonly INTENSE_WHITE: AnsiColor;
}export interface AnsiOptions {
weight: "normal" | "bold" | "dim";
italic: boolean;
underline: "none" | "single" | "double";
blink: boolean;
strike: boolean;
overline: boolean;
reverse: boolean;
underlineColor: AnsiColor;
foregroundColor: AnsiColor;
backgroundColor: AnsiColor;
}- All options are overridable at any depth of nesting.
- Instances store partial options; unspecified fields inherit from their parent.
A concrete AnsiOptions object providing library defaults (e.g., no formatting, default colors). You can pass it if you need to control the top-most defaults.
Predefined color constants:
Ansi.STANDARD_*: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITEAnsi.INTENSE_*: same 8 colors, high-intensity versions
Programmatic helpers:
Ansi.GRAYSCALE(n: number)– n ∈ [0..23]Ansi.BASIC_RGB(r: number, g: number, b: number)– each ∈ [0..5]Ansi.TRUE_COLOR(r: number, g: number, b: number)– each ∈ [0..255]
All return an AnsiColor.
Creates a template tag pre-configured with options:
const red = Ansi.tt({ foregroundColor: Ansi.STANDARD_RED });
const boldGreen = Ansi.tt({
weight: "bold",
foregroundColor: Ansi.STANDARD_GREEN,
});
console.log(red`Hello ${boldGreen`World`}`.toString());Returns a final string (with or without escape codes, depending on the implementation you’re using). Pass resolvedOuterOptions if you want to control the outer baseline (e.g., already-applied styles).
import { ansi } from "ansi-escape-code/proxy";ansi is both:
- a template tag:
ansi.bold.red`...strings` - a chainable factory:
ansi.color(...).backgroundColor(...).underlineColor(...)...
type AnsiTT = AnsiFactory & AnsiTemplateTag.
All of these return another AnsiTT, so you can keep chaining and end with a template literal:
export interface AnsiFactory {
normal: AnsiTT;
bold: AnsiTT;
dim: AnsiTT;
italic: AnsiTT;
no_italic: AnsiTT;
underline: AnsiTT;
double_underline: AnsiTT;
no_underline: AnsiTT;
blink: AnsiTT;
no_blink: AnsiTT;
strike: AnsiTT;
no_strike: AnsiTT;
overline: AnsiTT;
no_overline: AnsiTT;
reverse: AnsiTT;
no_reverse: AnsiTT;
// Foreground colors
color: (color: AnsiColor) => AnsiTT;
default: AnsiTT;
black: AnsiTT;
red: AnsiTT;
green: AnsiTT;
yellow: AnsiTT;
blue: AnsiTT;
magenta: AnsiTT;
cyan: AnsiTT;
white: AnsiTT;
intenseBlack: AnsiTT;
intenseRed: AnsiTT;
intenseGreen: AnsiTT;
intenseYellow: AnsiTT;
intenseBlue: AnsiTT;
intenseMagenta: AnsiTT;
intenseCyan: AnsiTT;
intenseWhite: AnsiTT;
grayscale: (n: number) => AnsiTT;
basicRgb: (r: number, g: number, b: number) => AnsiTT;
trueColor: (r: number, g: number, b: number) => AnsiTT;
// Background colors
backgroundColor: (color: AnsiColor) => AnsiTT;
backgroundDefault: AnsiTT;
backgroundBlack: AnsiTT;
backgroundRed: AnsiTT;
backgroundGreen: AnsiTT;
backgroundYellow: AnsiTT;
backgroundBlue: AnsiTT;
backgroundMagenta: AnsiTT;
backgroundCyan: AnsiTT;
backgroundWhite: AnsiTT;
backgroundIntenseBlack: AnsiTT;
backgroundIntenseRed: AnsiTT;
backgroundIntenseGreen: AnsiTT;
backgroundIntenseYellow: AnsiTT;
backgroundIntenseBlue: AnsiTT;
backgroundIntenseMagenta: AnsiTT;
backgroundIntenseCyan: AnsiTT;
backgroundIntenseWhite: AnsiTT;
backgroundGrayscale: (n: number) => AnsiTT;
backgroundBasicRgb: (r: number, g: number, b: number) => AnsiTT;
backgroundTrueColor: (r: number, g: number, b: number) => AnsiTT;
// Underline colors
underlineColor: (color: AnsiColor) => AnsiTT;
underlineDefault: AnsiTT;
underlineBlack: AnsiTT;
underlineRed: AnsiTT;
underlineGreen: AnsiTT;
underlineYellow: AnsiTT;
underlineBlue: AnsiTT;
underlineMagenta: AnsiTT;
underlineCyan: AnsiTT;
underlineWhite: AnsiTT;
underlineIntenseBlack: AnsiTT;
underlineIntenseRed: AnsiTT;
underlineIntenseGreen: AnsiTT;
underlineIntenseYellow: AnsiTT;
underlineIntenseBlue: AnsiTT;
underlineIntenseMagenta: AnsiTT;
underlineIntenseCyan: AnsiTT;
underlineIntenseWhite: AnsiTT;
underlineGrayscale: (n: number) => AnsiTT;
underlineBasicRgb: (r: number, g: number, b: number) => AnsiTT;
underlineTrueColor: (r: number, g: number, b: number) => AnsiTT;
}Example:
console.log(
ansi.bold.underline.color(ansi.TRUE_COLOR(255, 128, 0))
.backgroundBlue`🔥 Warning`.toString()
);You can render an Ansi object without ANSI escape codes by using NoopAnsi. This is useful for environments that cannot display escape sequences (logs, files, CI text captures, etc.).
import { Ansi } from "ansi-escape-code";
import { NoopAnsi } from "ansi-escape-code/NoopAnsi";
function printWithoutAnsiEscape(someAnsiObject: Ansi) {
console.log(new NoopAnsi({}, someAnsiObject).toString());
}- Every
Ansiinstance carries partial options and a list of parts (AnsiPart[]) that can themselves beAnsiinstances or any{ toString(): string }. - When calling
toString(), the library determines the difference between the currently active style and the child’s requested style, emitting only the necessary escape codes (and resets) at boundaries. - This is what enables deep nesting without style leakage or unnecessary resets.
Key exported types:
export type AnsiPart = Ansi | { toString(): string };
export interface AnsiOptions {
weight: "normal" | "bold" | "dim";
italic: boolean;
underline: "none" | "single" | "double";
blink: boolean;
strike: boolean;
overline: boolean;
reverse: boolean;
underlineColor: AnsiColor;
foregroundColor: AnsiColor;
backgroundColor: AnsiColor;
}
export type AnsiTemplateTag = (
strings: TemplateStringsArray,
...values: readonly AnsiPart[]
) => Ansi;
export interface AnsiFactory {
/* see full list above */
}
type AnsiTT = AnsiFactory & AnsiTemplateTag;Q: Why do I need to call .toString()?
A: To make composition easy and avoid magic side effects. You can always delay final rendering until you actually need the string.
Q: Can I mix plain strings with Ansi parts?
A: Yes — any object with a toString() method (including plain strings) can be interpolated.
Q: How do I completely disable colors?
A: Use ansi-escape-code/node / ansi-escape-code/proxy-node to automatically strip when not a TTY, or manually wrap with NoopAnsi / provide a noop renderer.
Q: How expensive is nesting?
A: The library computes minimal transitions between styles. Unless you’re generating huge volumes per frame, you’ll be fine.
MIT