nanospinner
A minimal, zero-dependency terminal spinner for Rust CLI applications.
Inspired by the nanospinner npm package, nanospinner gives you a lightweight animated spinner using only the Rust standard library — no heavy crates, no transitive dependencies, under 200 lines of code.
Motivation
Most Rust spinner crates (like indicatif or spinoff) are feature-rich but pull in multiple dependencies, increasing compile times and binary size. If all you need is a simple spinner with a message, a success state, and a failure state, those crates are overkill.
nanospinner solves this by providing the essentials and nothing more:
- Zero external dependencies (only
std) - Tiny footprint (< 200 LOC)
- Simple, ergonomic API
- Thread-safe with clean shutdown
Comparison
| Crate | Dependencies | Lines of Code | Clean Build Time | Customizable Frames | Progress Bars |
|---|---|---|---|---|---|
nanospinner |
0 | ~200 | ~0.1s | Default Braille set | No |
spinoff |
3+ | ~1,000+ | ~1.2s | Yes (80+ sets) | No |
indicatif |
5+ | ~5,000+ | ~1.4s | Yes | Yes |
Build times measured from a clean cargo build --release on macOS aarch64 (Apple Silicon). Your numbers may vary by platform.
nanospinner is for when you want a spinner and nothing else.
Features
- Animated Braille dot spinner (
⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏) - Colored finalization: green
✔for success, red✖for failure - Update the message while the spinner is running
- Custom writer support (stdout, stderr, or any
io::Write + Send) - Automatic cleanup via
Drop— no thread leaks if you forget to stop - Automatic TTY detection — ANSI codes and animation are skipped when output is piped or redirected
Quick Start
Add nanospinner to your Cargo.toml:
[]
= "0.1.0"
use Spinner;
use thread;
use Duration;
Usage
Create and start a spinner
let handle = new.start;
Finalize with success or failure
handle.success; // ✔ Downloading files...
handle.fail; // ✖ Downloading files...
Finalize with a replacement message
handle.success_with; // ✔ Done!
handle.fail_with; // ✖ Connection timed out
Update the message mid-spin
let handle = new.start;
sleep;
handle.update;
sleep;
handle.success_with;
Write to a custom destination
use io;
let handle = with_writer.start;
sleep;
handle.success;
Stop without a symbol
let mut handle = new.start;
sleep;
handle.stop; // clears the line, no symbol printed
Piped / non-TTY output
When stdout isn't a terminal (e.g. piped to a file or another program), nanospinner automatically skips the animation and ANSI color codes. The final result is printed as plain text:
|
No configuration needed — Spinner::new() detects this automatically. If you're using a custom writer and want to force TTY behavior, use with_writer_tty:
let handle = with_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 as dependency-free as possible.
License
This project is licensed under the MIT License.