██░░ nanoprogress
A minimal, zero-dependency terminal progress bar for Rust CLI applications.

Inspired by the nanospinner package, nanoprogress gives you a lightweight determinate progress bar using only the Rust standard library — no heavy crates, no transitive dependencies, under 300 lines of code.
Nano Crate Family
Part of the nano crate family — minimal, zero-dependency building blocks for CLI apps in Rust:
- nanocolor — terminal colors and styles
- nanospinner — terminal spinners
- nanoprogress — progress bars
- nanologger — minimal logger
- nanotime — time utilities
Motivation
Most Rust progress bar crates (like indicatif) are feature-rich but pull in multiple dependencies, increasing compile times and binary size. If all you need is a simple progress bar with a count, percentage, and success/failure states, those crates are overkill.
nanoprogress solves this by providing the essentials and nothing more:
- Zero external dependencies (only
std) - Tiny footprint (< 300 LOC)
- Simple, ergonomic builder API
- Thread-safe — share across threads with
Clone - Automatic cleanup via
Drop
Comparison
| Crate | Dependencies | Lines of Code | Clean Build Time | Thread-safe |
|---|---|---|---|---|
nanoprogress |
0 | ~200 | ~0.5s | Yes |
progress_bar |
0 | ~430 | ~3.4s | No |
pbr |
3 | ~730 | ~4.3s | No |
linya |
1 | ~180 | ~3.8s | Yes (Mutex) |
kdam |
3+ | ~2,000 | ~2.2s | Yes |
indicatif |
5+ | ~4,500 | ~1.9s | Yes |
Build times measured from a clean cargo build --release on macOS aarch64 (Apple Silicon). Your numbers may vary by platform.
nanoprogress is for when you want a single progress bar with zero compile-time cost and nothing else. If you need multi-bars, templates, or ETA estimation, reach for indicatif or kdam.
Features
- Determinate progress bar with fill/empty characters (
█░) - Percentage display and current/total count
- Colored finalization: green
✔for success, red✖for failure - Customizable bar width, fill character, and empty character
- Update the message while the bar is running
- Custom writer support (stdout, stderr, or any
io::Write + Send) - Automatic cleanup via
Drop— no dangling cursor if you forget to finalize - Automatic TTY detection — ANSI codes are skipped when output is piped or redirected
Quick Start
use ProgressBar;
use thread;
use Duration;
Usage
Create and start a progress bar
let bar = new
.message
.start;
Increment progress
bar.tick; // increment by 1
bar.tick; // increment by 10 — clamped to total
Finalize with success or failure
bar.success; // ✔ All done
bar.fail; // ✖ Something broke
Update the message mid-progress
let bar = new.message.start;
for i in 0..200
bar.success;
Customize the bar appearance
let bar = new
.width
.fill
.empty
.message
.start;
Write to a custom destination
use io;
let bar = new
.writer
.start;
Share across threads
use thread;
let bar = new.start;
let handles: = .map.collect;
for h in handles
bar.success;
Piped / non-TTY output
When stdout isn't a terminal (e.g. piped to a file or another program), nanoprogress automatically skips ANSI codes and prints each update on a new line:
|
No configuration needed — custom writers default to non-TTY mode. To force TTY behavior:
let bar = new
.writer
.tty
.start;
Contributing
Contributions are welcome. To get started:
- Fork the repository
- Create a feature branch (
git checkout -b my-feature) - Make your changes
- Run the tests:
cargo test - Submit a pull request
Please keep changes minimal and focused. This crate's goal is to stay small and dependency-free.
License
This project is licensed under the MIT License.