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-fetchercopyThe 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.
| Package | What it does | Size |
|---|---|---|
nano-clsx | Conditional class names | under 300b |
nano-slug | URL-safe slugs, accents handled | under 400b |
nano-safe-storage | localStorage that survives being full, with TTL | under 600b |
nano-human-bytes | Bytes to "1.5 KB", and back again | under 500b |
tiny-debounce-throttle | Debounce and throttle, with cancel and flush | under 600b |
@arnelirobles/tiny-time-ago | Relative time, with locales | under 500b |
click-outside-lite | Close the dropdown when they click away | under 400b |
deep-merge-lite | Deep merge with array strategies | under 700b |
deep-clean-obj | Strip null and undefined out of an object | under 500b |
resilient-fetcher | fetch with retry and timeout | under 800b |
mermaid-export | Export Mermaid diagrams |
What using them looks like
import { clsx } from 'nano-clsx';
clsx('btn', { active: isActive, disabled: !enabled }); // 'btn active'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 minutesimport { debounce } from 'tiny-debounce-throttle';
const search = debounce(fetchResults, 300, { maxWait: 1000 });
search('query');
search.cancel(); // and you can take it backimport { formatBytes } from 'nano-human-bytes';
formatBytes(1536); // '1.5 KB'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.