Add a Progress Bar for Streamed Work
Render progress to stderr so it does not pollute stdout, and suppress it when output is not a terminal.
Task
Add a progress indicator that writes to stderr and degrades when piped.
When to use this
A long import prints progress to stdout, corrupting the machine-readable output downstream.
Prerequisites
- A CLI performing iterative work
- Access to stderr
- TTY detection
Steps
1. Render progress on stderr only when interactive.
const show = process.stderr.isTTY;
function progress(done, total) {
if (!show) return;
const pct = Math.floor((done / total) * 100);
process.stderr.write(`\r${pct}% (${done}/${total})`);
}Expected output: Progress updates overwrite themselves on stderr without touching stdout.
2. Clear the line when finished.
function finish() { if (show) process.stderr.write('\n'); }Expected output: The final newline keeps the terminal prompt clean.
Troubleshooting
Problem
Progress in logs
Solution
Only render when stderr is a TTY; log a periodic summary otherwise.
Problem
Carriage returns corrupt output
Solution
Never write progress to stdout; keep data and UI separated.
Limitations
- Rendering per item is costly; throttle updates.
- Not accessible to screen readers.
- Does not estimate remaining time well for uneven work.
What this recipe does
Add a progress indicator that writes to stderr and degrades when piped.
A long import prints progress to stdout, corrupting the machine-readable output downstream.
Prerequisites
- A CLI performing iterative work
- Access to stderr
- TTY detection
Procedure
- Render progress on stderr only when interactive.
const show = process.stderr.isTTY;
function progress(done, total) {
if (!show) return;
const pct = Math.floor((done / total) * 100);
process.stderr.write(`\r${pct}% (${done}/${total})`);
}
Expected result: Progress updates overwrite themselves on stderr without touching stdout.
- Clear the line when finished.
function finish() { if (show) process.stderr.write('\n'); }
Expected result: The final newline keeps the terminal prompt clean.
How to confirm it worked
Run the commands above and confirm every expected result in order. If a step does not produce the stated output, use the troubleshooting notes before continuing. A passing run shows interactive runs show an updating bar on stderr, while piped runs keep stdout clean.
Troubleshooting
Progress in logs — Only render when stderr is a TTY; log a periodic summary otherwise.
Carriage returns corrupt output — Never write progress to stdout; keep data and UI separated.
Limitations
- Rendering per item is costly; throttle updates.
- Not accessible to screen readers.
- Does not estimate remaining time well for uneven work.