mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmCLI & Toolingupdated 22 Sept 2026

nanospinner review

nanospinner 1.2.2 animates one status line in a Node.js terminal, then replaces it with a success, error, warning, info, or custom mark. It includes text updates, frame and color choices, interval control, and stream selection, with picocolors as its only dependency. The current release still has one-spinner scope; multi-spinner support remains an unchecked README roadmap item. Our install confirmed bundled TypeScript declarations and Node-only behavior, which fits small CLIs but not browser interfaces or task orchestration.

Verdict

Our nanospinner 1.2.2 install took 0.7 seconds, occupied 1 MB across 2 packages, and had 0 audit findings, but its browser build failed and it manages process signals while active. It is a good fit for one short Node CLI task; choose Listr2 when progress display becomes application state.

We installed it

Lab card: what happened when we installed nanospinnerScreenshot of nanospinner documentation
Install✓ · 0.7s2 packages on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browsern/acould not be bundled for the browser (Node-only code, most likely)
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does nanospinner install cleanly?

Yes. In a fresh container with an empty cache, npm install nanospinner finished in 0.7s, leaving 2 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

Can nanospinner run in a browser?

Not directly: esbuild could not bundle it for the browser in our run, which normally means it depends on Node built-ins. Use it on the server, or find a browser-targeted alternative.

Does nanospinner work with both ESM and CommonJS?

Yes. Both import 'nanospinner' and require('nanospinner') worked in Node 22 in our run. The package is published as CommonJS.

Does nanospinner include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

nanospinner or ora: which should you use?

ora: Use it for a more mature spinner with broader terminal behavior and options. Our nanospinner 1.2.2 install took 0.7 seconds, occupied 1 MB across 2 packages, and had 0 audit findings, but its browser build failed and it manages process signals while active.

When should you not use nanospinner?

Several tasks must animate at once; multi-spinner support is still unfinished in the README roadmap.

API stability4/5Version 1.2.2 exposes a small chainable spinner API with `start`, `stop`, status methods, `update`, `clear`, and `reset`. Bundled declarations describe the accepted strings and option objects. Two implementation details weaken the contract: stream changes in `update` are not applied, and signal handling is an implicit side effect of starting animation.
Docs3/5The README gives runnable ESM examples for every public method, lists CommonJS and TypeScript support, and shows custom text, marks, frames, intervals, colors, and streams. It does not document exit codes, signal listeners, stdout-based TTY detection, non-TTY rendering, or the difference between `reset` and a full stop, all of which affect real CLI behavior.
Maintenance3/5npm published 1.2.2 on 2024-12-09, and GitHub reports the last push on 2024-12-10. The repository is not archived and currently shows 4 open issues and pull requests. Those dates show a completed recent release cycle, but the remaining multi-spinner roadmap item and no later source activity make this a steady small utility rather than an actively expanding project.
Ecosystem3/5npm recorded 3,134,552 weekly downloads through 2026-08-24, and the package works from CommonJS, ESM interop, and TypeScript projects. Its single picocolors dependency keeps integration simple. The surrounding ecosystem is intentionally limited: no task-runner adapters, multi-spinner renderer, log coordination, or browser target is included.

Use it if

  • One asynchronous CLI operation needs a compact progress indicator and a final status line.
  • A non-TTY run should print one plain line instead of scheduling animation frames.
  • Custom frames, colors, intervals, and a chosen output stream cover the display requirements.
  • Bundled TypeScript declarations and a single small runtime dependency matter.
Skip it if

Setup reality

Our nanospinner 1.2.2 install completed in 0.7 seconds and left 2 packages using 1 MB on disk. The package is 40 KB unpacked, has 1 direct dependency and 0 peers, and returned 0 known vulnerabilities from npm audit. No native compilation, credential, or configuration file is involved.

The package is CommonJS without an exports map. Both require() and ESM import worked on Node 22, and declaration files are included. Create the spinner with its output stream and then call start(). Although the TypeScript option shape lets update() receive a stream, the 1.2.2 implementation does not replace the stream there.

Output defaults to stderr, but TTY capability is calculated from stdout file descriptor 1. Redirected stdout can therefore disable animation while stderr remains interactive. In CI or another non-TTY process, start() writes one hyphen-prefixed line and schedules no frames. clear() still emits cursor-control bytes, so avoid calling it in plain log mode.

An active spinner installs SIGINT and SIGTERM listeners that stop the display and exit with 130 or 143. Finish every path with success(), error(), warn(), info(), or stop('message') so the interval and listeners are removed. reset() cancels the timer but does not provide the same complete cleanup. Our browser build failed, confirming that its output logic belongs in Node terminals.

Patterns

Start and finish one task show-basic-spinner

import { createSpinner } from 'nanospinner'

const spinner = createSpinner('Downloading release').start()
await downloadRelease()
spinner.success({ text: 'Release downloaded' })

Call a terminal status method on every completion path so the interval and signal listeners are removed.

Replace animation with an error line report-task-failure

const spinner = createSpinner('Publishing').start()

try {
  await publish()
  spinner.success('Published')
} catch (error) {
  spinner.error('Publish failed')
  throw error
}

`error()` stops the timer and prints a failure mark; it does not throw or set the process exit code for your task.

Change text while work continues update-spinner-text

const spinner = createSpinner('Reading manifest').start()

const manifest = await readManifest()
spinner.update(`Installing ${manifest.dependencies.length} packages`)
await installDependencies(manifest)
spinner.success('Dependencies installed')

Version 1.2.2 applies text, color, frames, and interval updates, but does not switch the active stream through `update()`.

Provide frames and frame timing customize-animation

const spinner = createSpinner('Waiting for worker', {
  frames: ['.', '..', '...'],
  interval: 120,
  color: 'cyan',
}).start()

await waitForWorker()
spinner.success('Worker ready')

Short intervals generate more terminal writes; non-TTY mode prints once and does not animate those frames.

Select stdout when creating the spinner choose-output-stream

const spinner = createSpinner('Generating report', {
  stream: process.stdout,
}).start()

await generateReport()
spinner.success('Report generated')

TTY detection still reads file descriptor 1 in 1.2.2, regardless of the stream supplied to the spinner.

Finish with a warning status finish-with-warning

const spinner = createSpinner('Checking configuration').start()
const missing = await findOptionalSettings()

if (missing.length) {
  spinner.warn(`Missing optional settings: ${missing.join(', ')}`)
} else {
  spinner.success('Configuration complete')
}

A warning is only display output. The caller must decide whether the command should return a nonzero exit code.

Finish with an informational line finish-with-info

const spinner = createSpinner('Looking for updates').start()
const update = await findUpdate()

update
  ? spinner.info(`Version ${update.version} is available`)
  : spinner.success('Already up to date')

`info()` stops animation and writes one final line, making it suitable for a completed neutral outcome.

Print a custom completion mark use-custom-final-mark

const spinner = createSpinner('Skipping cached files').start()
await inspectCache()
spinner.stop({
  text: 'Cache is current',
  mark: '=',
  color: 'gray',
})

Keep the mark short because cursor clearing assumes a single terminal status line.

Check whether animation is active check-spinner-state

const spinner = createSpinner('Connecting').start()

try {
  await connect()
  if (spinner.isSpinning()) spinner.success('Connected')
} finally {
  if (spinner.isSpinning()) spinner.stop('Connection cancelled')
}

Use the exposed state for display decisions, not as proof that the underlying asynchronous operation is running.

Reuse one spinner sequentially reuse-spinner-instance

const spinner = createSpinner('Step one').start()
await stepOne()
spinner.success('Step one done')

spinner.reset().start('Step two')
await stepTwo()
spinner.success('Step two done')

The API models one active status at a time; finish the first operation before starting the next on the same instance.

Load nanospinner from CommonJS use-commonjs

const { createSpinner } = require('nanospinner')

const spinner = createSpinner('Building').start()
build().then(
  () => spinner.success('Built'),
  error => {
    spinner.error('Build failed')
    throw error
  },
)

CommonJS `require()` worked in our Node 22 sandbox despite the README leading with ESM syntax.

Disable animation for deterministic logs disable-spinner-explicitly

const quiet = process.env.CI || process.argv.includes('--json')
const spinner = quiet ? null : createSpinner('Loading').start()

const data = await loadData()
spinner?.success('Loaded')
if (process.argv.includes('--json')) console.log(JSON.stringify(data))

A disabled or non-TTY spinner should still produce a useful final line rather than cursor-control output.

Alternatives

PackageRegistryPick it when
oranpmUse it for a more mature spinner with broader terminal behavior and options.
listr2npmUse it for multiple, nested, concurrent, or persistent CLI tasks.
cli-spinnersnpmUse it when you only need frame definitions and will own all rendering yourself.

More cli & tooling guides

chalk · commander · typescript · esbuild · yargs · click · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.