Skip to content

The nano libraries

Eleven JavaScript utilities with no runtime dependencies. The largest is 800 bytes. Each one does exactly one thing, and you install only the one you need.

npm install nano-clsx nano-slug resilient-fetchercopy

The problem

You need to join a few class names conditionally. So you install clsx. Then you need a debounce, so you reach for lodash and, if you are not careful with your imports, you have just added seventy kilobytes to a page to delay a function call by three hundred milliseconds.

None of these tasks are hard. Every one of them is a handful of lines. But writing them again in every project is tedious, and copying them between projects means you now maintain nine copies of the same function.

These are those handful of lines, published, tested, and small enough that the dependency is not a decision you have to think about.

The set

Every package is independent. Take one, take three, ignore the rest.

PackageWhat it doesSize
nano-clsxConditional class namesunder 300b
nano-slugURL-safe slugs, accents handledunder 400b
nano-safe-storagelocalStorage that survives being full, with TTLunder 600b
nano-human-bytesBytes to "1.5 KB", and back againunder 500b
tiny-debounce-throttleDebounce and throttle, with cancel and flushunder 600b
@arnelirobles/tiny-time-agoRelative time, with localesunder 500b
click-outside-liteClose the dropdown when they click awayunder 400b
deep-merge-liteDeep merge with array strategiesunder 700b
deep-clean-objStrip null and undefined out of an objectunder 500b
resilient-fetcherfetch with retry and timeoutunder 800b
mermaid-exportExport Mermaid diagrams

What using them looks like

ts
import { clsx } from 'nano-clsx';

clsx('btn', { active: isActive, disabled: !enabled });   // 'btn active'
ts
import { createStorage } from 'nano-safe-storage';

// localStorage, except it does not throw when the quota is full
// and it can expire things on its own.
const storage = createStorage({ prefix: 'app_', ttl: 3600 });
storage.set('token', 'abc', { ttl: 900 });   // gone in fifteen minutes
ts
import { debounce } from 'tiny-debounce-throttle';

const search = debounce(fetchResults, 300, { maxWait: 1000 });
search('query');
search.cancel();   // and you can take it back
ts
import { formatBytes } from 'nano-human-bytes';

formatBytes(1536);   // '1.5 KB'
ts
import { timeAgo, createTimeAgo } from '@arnelirobles/tiny-time-ago';

timeAgo(Date.now() - 60000);   // '1 minute ago'

const ago = createTimeAgo('es');
ago(new Date('2024-12-23'));   // 'hace 1 día'

One of these is scoped

tiny-time-ago ships as @arnelirobles/tiny-time-ago, because the short name was already taken. The other ten install under their plain names.

Where they fit

Anywhere you were about to pull in a large utility library for one function. They also hold up the rest of my own work: feed-slurp is built on resilient-fetcher and nano-safe-storage, which is the honest test of whether a utility is any good. I have to use it.

All eleven, with the full API for each, on GitHub →

Brewed in the baryo ☕ · Released under the MIT License.