Expand description
A minimal, zero-dependency terminal spinner for Rust CLI applications.
nanospinner provides lightweight animated spinners for giving users
feedback during long-running CLI operations. Run a single spinner or
multiple concurrent spinners — each on a background thread so your main
logic stays unblocked.
Built with only the Rust standard library — no transitive dependencies, fast compile times, and a tiny binary footprint.
§Quick start
use nanospinner::Spinner;
use std::thread;
use std::time::Duration;
let handle = Spinner::new("Loading...").start();
thread::sleep(Duration::from_secs(2));
handle.success();§Usage
§Single spinner
§Finishing with success or failure
Use SpinnerHandle::success for a green ✔ or SpinnerHandle::fail
for a red ✖. Both consume the handle and stop the animation.
let handle = Spinner::new("Deploying...").start();
thread::sleep(Duration::from_secs(1));
handle.fail(); // ✖ Deploying...You can also replace the message at finalization:
let handle = Spinner::new("Compiling...").start();
thread::sleep(Duration::from_secs(2));
handle.success_with("Compiled in 2.1s"); // ✔ Compiled in 2.1s§Updating the message mid-spin
let handle = Spinner::new("Step 1...").start();
thread::sleep(Duration::from_secs(1));
handle.update("Step 2...");
thread::sleep(Duration::from_secs(1));
handle.success_with("All steps complete");§Custom writers
Write to stderr or any std::io::Write + Send target:
let handle = Spinner::with_writer("Processing...", std::io::stderr()).start();
thread::sleep(Duration::from_secs(1));
handle.success();§TTY detection
When stdout is not a terminal (e.g. piped to a file), nanospinner
automatically skips the animation and ANSI escape codes. The final
result is printed as plain text:
$ my_tool | cat
✔ Done!For custom writers you can force TTY behavior with
Spinner::with_writer_tty.
§Multiple spinners
MultiSpinner renders several spinner lines at once, each
independently updatable and finalizable. Use
MultiSpinnerHandle::add to dynamically append new lines — even
after the animation has started. Lines can be finished with
SpinnerLineHandle::success / SpinnerLineHandle::fail, or
silently dismissed with SpinnerLineHandle::clear — cleared lines
disappear and the remaining lines collapse together with no gap.
use nanospinner::MultiSpinner;
use std::thread;
use std::time::Duration;
let handle = MultiSpinner::new().start();
let line1 = handle.add("Compiling crate A...");
let line2 = handle.add("Compiling crate B...");
let line3 = handle.add("Checking crate C...");
thread::sleep(Duration::from_secs(2));
line1.success();
line2.fail_with("Crate B had errors.");
line3.clear(); // silently dismissed — no output
handle.stop();Each SpinnerLineHandle is Send, so you can move it to another
thread and finalize or clear it from there. For custom output targets
or explicit TTY control, see MultiSpinner::with_writer and
MultiSpinner::with_writer_tty.
§Features
- Zero dependencies — only
std - Braille-dot animation (
⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏) on a single line - Multiple concurrent spinners via
MultiSpinner— each line independently updatable and finalizable, or silently dismissible viaSpinnerLineHandle::clear - Update the message while spinning via
SpinnerHandle::update - Finish with
SpinnerHandle::success(✔) orSpinnerHandle::fail(✖) - Replacement messages via
SpinnerHandle::success_with/SpinnerHandle::fail_with - Pluggable writer for testing or custom output targets
- Automatic TTY detection — ANSI codes and animation are skipped when output is piped or redirected
- Clean shutdown via
Drop— no thread leaks if you forget to stop
Structs§
- Multi
Spinner - A builder for a multi-spinner group that manages multiple concurrent spinners on separate terminal lines.
- Multi
Spinner Handle - Handle returned by
MultiSpinner::startfor managing a running multi-spinner group. - Spinner
- A builder for configuring and starting a terminal spinner.
- Spinner
Handle - Handle for controlling a running spinner.
- Spinner
Line Handle - Handle for controlling a single spinner line within a multi-spinner group.