pin: A Small CLI Spinner for Go
· go
I wrote pin, a terminal spinner for Go that uses nothing but the standard library. It exists because most Go CLI spinner libraries have the same problem: they want to be frameworks.
You pull in one dependency for a tiny rotating character in your terminal, and suddenly you’re carrying transitive dependencies, ANSI abstractions you didn’t ask for, and an API surface that assumes you’re building a dashboard.
All I wanted was a dot that spins.
Zero Dependencies
pin does colors, prefixes, separators and live message updates, and that’s it. The part I care about most is that it imports only the Go standard library (zero external dependencies).
Dependencies are promises. Every go get you run is a bet that some stranger’s weekend project will keep working the way you expect. For something as trivial as a spinner, that bet is genuinely not worth taking. A package with no dependencies is easy to audit and easy to vendor, and it will never surprise you with a breaking change three levels deep in your dependency tree.
Features
The list is short on purpose:
- You can set the spinner color, the text color, a prefix and a separator. These are the small formatting knobs you actually need.
- You can update the message while the work is in progress, because “Loading…” is not helpful for thirty seconds straight.
- When stdout is not a terminal,
pinturns the animation off so you don’t get escape codes in your log files (try./myapp | tee output.txt). - It uses the standard library only. I keep saying it because that is the point.
Quick Start
Install it with go get:
go get github.com/yarlson/pinHere is a complete program that starts a spinner, changes its message halfway through the work, and stops it:
package main
import (
"context"
"time"
"github.com/yarlson/pin"
)
func main() {
p := pin.New("Loading...",
pin.WithSpinnerColor(pin.ColorCyan),
pin.WithTextColor(pin.ColorYellow),
)
cancel := p.Start(context.Background())
defer cancel()
// Simulate work
time.Sleep(3 * time.Second)
p.UpdateMessage("Almost done...")
time.Sleep(2 * time.Second)
p.Stop("Done!")
}pin.New takes the first message and the color options. Start takes a context and gives you back a cancel function, UpdateMessage swaps the text while the spinner keeps going, and Stop takes the final message.
Look, it’s a spinner. The API should fit in your head in thirty seconds, and this one does.
Why Not Just Use a Popular Library?
Fair question. If you’re already deep in a CLI framework that bundles its own spinner, use that. Seriously.
But maybe you’re writing a small tool, such as a deployment script, a code generator or a quick utility, and you want a clean loading indicator without adopting someone else’s opinions about terminal rendering. pin exists for exactly that moment. A spinner with no dependencies is a spinner that does not break because somebody else shipped a bad release on a Friday afternoon.
The source is on GitHub at github.com/yarlson/pin. It’s small enough that you can read the whole thing in one sitting.