A zero-dependency Node.js utility for colored logs, spinners, progress bars, tables, tree views, diffs, and OS notifications — all without looking up a single ANSI code.
You know the drill. You're deep into a Node.js CLI tool or a long-running script. The terminal output looks like this:
processing...
done
error
done
Completely useless. No color. No structure. No idea what happened.
So you reach for chalk, then ora, then cli-table3, then diff, and suddenly your package.json has five new dependencies for the sole purpose of making console.log bearable.
budgie-console is a single package that handles all of it — with zero external dependencies.
npm install budgie-console
const Console = require('budgie-console');
That's the whole setup. Let's look at what you get.
The basics: colored log levels
Four methods that do what you actually want from a log level:
Console.success('Server started on port 3000'); // ✔ green
Console.error('Database connection refused'); // ✖ red
Console.warn('NODE_ENV is not set'); // ⚠ yellow
Console.info('Running Node ' + process.version); // ℹ cyan
And the core log() method if you want full control over colors yourself:
Console.log(Console.Bright + Console.FgCyan, 'Bold cyan');
Console.log(Console.Underscore + Console.BgYellow, 'Underlined on yellow');
Console.log(Console.FgRed, 'Custom red message');
All ANSI color and style constants are exposed directly — FgRed, BgGreen, Bright, Dim, Underscore, and the rest — so you can compose them freely without memorising escape sequences. Colors automatically disable themselves when NO_COLOR is set or the terminal is dumb.
Spinner — with a completion message
The spinner animates in-place using \r and stops cleanly when your statusFn returns false. The new doneMessage parameter means you don't need to awkwardly call Console.success() yourself afterwards and risk a race condition:
let running = true;
Console.spinner(
['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'],
'Fetching remote config...',
80,
() => running,
'Config loaded!' // ← printed atomically when spinner stops
);
fetchConfig().then(() => { running = false; });
✔ Config loaded!
It also returns a cancel function if you need to stop it imperatively:
const stop = Console.spinner(frames, 'Building...', 100, () => true);
// later:
stop();
Progress bar — with cleanup
Call progress() from any loop or interval. When current >= total, it prints a newline so subsequent output starts on a fresh line (not tacked onto the end of the bar line — a subtle bug that's been fixed). Optional doneMessage here too:
let i = 0;
const iv = setInterval(() => {
Console.progress(i, 50, 30, Console.FgCyan, 'Upload complete');
if (++i > 50) clearInterval(iv);
}, 40);
[████████████████████████████░░] 93%
✔ Upload complete
Table — handles any cell type
Render bordered tables from a 2D array. Pass an optional header row as the second argument. Cell values don't need to be strings — numbers, booleans, null, and undefined are all coerced safely, so you can pass data directly from your objects without .toString()-ing everything:
Console.table(
[
['Alice', 28, true, 'Engineer'],
['Bob', 34, false, 'Designer'],
['Charlie', null, true, 'Intern' ],
],
['Name', 'Age', 'Active', 'Role']
);
┌─────────┬──────┬────────┬──────────┐
│ Name │ Age │ Active │ Role │
├─────────┼──────┼────────┼──────────┤
│ Alice │ 28 │ true │ Engineer │
│ Bob │ 34 │ false │ Designer │
│ Charlie │ null │ true │ Intern │
└─────────┴──────┴────────┴──────────┘
Tree — pretty-print nested objects
Ever wanted something like util.inspect but actually readable in a terminal? Console.tree() renders any JS object or array as an indented tree with proper branch characters, like the Unix tree command. Primitives are color-coded: strings in green, numbers in cyan, booleans and null in yellow.
Console.tree({
server: {
host: 'localhost',
port: 3000,
ssl: false,
},
db: {
name: 'myapp_prod',
pool: 5,
replicas: ['db1', 'db2'],
},
}, 'config');
└── config
├── server
│ ├── host: "localhost"
│ ├── port: 3000
│ └── ssl: false
└── db
├── name: "myapp_prod"
├── pool: 5
└── replicas
├── 0: "db1"
└── 1: "db2"
Incredibly useful when debugging config objects, parsed JSON responses, or AST nodes. The second argument is the label for the root node — defaults to 'root'.
Diff — colored line-by-line comparison
No external dependency. Pure string comparison, line by line. Removed lines in red with -, added lines in green with +, unchanged lines dimmed. Perfect for showing what changed in a config file, an API response, or a generated file before writing it to disk:
const before = `
host: localhost
port: 3000
debug: true
`.trim();
const after = `
host: localhost
port: 8080
debug: true
logLevel: info
`.trim();
Console.diff(before, after);
host: localhost
- port: 3000
+ port: 8080
debug: true
+ logLevel: info
No LCS algorithm, no diffing library, no download. Works fine for config summaries, migration previews, and anything where you're comparing two versions of a string.
Notify — OS desktop notifications
When a long build finishes, you don't want to keep staring at the terminal. Console.notify() fires a native OS notification so you can switch tabs and come back when it's done:
Console.notify('Build complete', 'Your production bundle is ready.');
It shells out to osascript on macOS, notify-send on Linux, and PowerShell's NotifyIcon on Windows. No npm packages. If the underlying command isn't found, it warns to the terminal instead of throwing, so it degrades gracefully in CI or SSH sessions.
Linux note: requires
libnotify—sudo apt install libnotify-binon Debian/Ubuntu.
The utilities you already know
A few more methods that round out the toolkit:
Box — wraps text in a bordered box, good for section headers in verbose output:
Console.box('Deployment complete', Console.FgGreen);
┌──────────────────────┐
│ Deployment complete │
└──────────────────────┘
Divider — horizontal rule to visually separate output sections:
Console.divider(); // ──────────── (40 chars, dim)
Console.divider('═', 44, Console.FgCyan);
Prompt — async readline input, returns a Promise:
const name = await Console.prompt('Your name:');
Console.success(`Hello, ${name}`);
Clear — clears the terminal:
Console.clear();
Install and use
npm install budgie-console
const Console = require('budgie-console');
Console.success('Ready');
Console.table([['key', 'value']], ['Param', 'Result']);
Console.tree({ build: { env: 'production', minify: true } });
Console.notify('Done', 'Script finished');
Zero config. Zero dependencies. Requires Node.js >=14. ANSI codes work out of the box on macOS and Linux; on Windows they work in Windows Terminal and VS Code's integrated terminal.
Source is on GitHub — yashdatir/budgie-console. Issues, PRs, and feedback welcome.
What do you reach for when you need terminal output to not look terrible? Drop it in the comments — always curious what other people are using.
Top comments (0)