The 50 Most Underrated npm Packages in 2026
·PkgPulse Team
TL;DR
The best npm packages aren't always the most famous ones. This list features packages with <500K weekly downloads but outsized value — tiny utilities that expert developers swear by, alternatives to bloated standards, and new packages that solve old problems better. Each one is maintained, typed, and under-installed relative to its quality.
Key Takeaways
- Many expert-level picks have under 100K downloads but solve specific problems elegantly
- Zero-dep packages dominate this list — small, focused, zero supply chain risk
- TypeScript-first — every package on this list ships its own types
- Recency matters — most of these are 2022+ packages (modern APIs and patterns)
Utilities
1. defu — 160K downloads/week
// Recursive default merge: only fills in missing values (not override)
import { defu } from 'defu';
const config = defu(userConfig, {
port: 3000,
host: 'localhost',
debug: false,
});
// Unlike Object.assign or spread: defu goes DEEP
// Unlike deepmerge: defu ONLY fills in undefined, doesn't override existing
2. pathe — 800K downloads/week
// Path utilities like Node.js `path` but cross-platform (always uses /)
import { join, resolve, dirname } from 'pathe';
// No more "why does this work on Mac but not Windows?" path issues
// Used by Nuxt, Vite, many UnJS packages internally
3. ohash — 400K downloads/week
// Fast object hashing — same hash for equivalent objects regardless of key order
import { hash, objectHash } from 'ohash';
hash({ b: 2, a: 1 }) === hash({ a: 1, b: 2 }) // true
// Use for cache keys, deduplication, change detection
4. scule — 300K downloads/week
// String case conversion: camelCase, snake_case, PascalCase, kebab-case
import { camelCase, snakeCase, pascalCase, kebabCase } from 'scule';
camelCase('hello-world') // 'helloWorld'
snakeCase('helloWorld') // 'hello_world'
// 0 dependencies, tiny bundle
5. perfect-debounce — 180K downloads/week
// Async-aware debounce: if the function is async, it queues correctly
import { debounce } from 'perfect-debounce';
const search = debounce(async (query: string) => {
return await api.search(query);
}, 300);
// Handles: multiple rapid calls, awaited return values, cancellation
Data & Types
6. type-fest — 3.5M downloads/week (underrated despite size)
// Huge collection of TypeScript utility types not in the standard library
import type { Simplify, Jsonify, SetOptional, RequireAtLeastOne } from 'type-fest';
type ApiUser = Simplify<User & { token: string }>; // Flattens intersection
type SetName = SetOptional<User, 'avatar' | 'bio'>; // Make fields optional
type Config = RequireAtLeastOne<Options, 'url' | 'path'>; // One required
7. remeda — 280K downloads/week
// Type-safe Lodash alternative: full TypeScript inference, tree-shakeable
import * as R from 'remeda';
const result = R.pipe(
users,
R.filter(u => u.active),
R.map(u => u.name),
R.sort(R.ascend(R.identity)),
);
// Lodash has types, but remeda's are tighter and functional-pipeline-friendly
8. ts-reset — 400K downloads/week
// Makes TypeScript stricter in specific ways:
import '@total-typescript/ts-reset';
// - JSON.parse() returns unknown (not any)
// - Array.filter(Boolean) removes undefined from type
// - fetch().json() returns unknown
// No runtime code — just type declarations that improve type safety
9. zod-to-json-schema — 1M downloads/week
// Convert Zod schemas to JSON Schema (for OpenAPI, form libraries, etc.)
import { zodToJsonSchema } from 'zod-to-json-schema';
import { z } from 'zod';
const userSchema = z.object({ name: z.string(), age: z.number() });
const jsonSchema = zodToJsonSchema(userSchema);
// { type: 'object', properties: { name: { type: 'string' }, ... } }
// Use for: Swagger/OpenAPI docs, JSON Schema validation, tRPC meta
Node.js / Server
10. c12 — 400K downloads/week
// Config loader: reads from multiple sources (env, config files, pkg.json)
import { loadConfig } from 'c12';
const { config } = await loadConfig({
name: 'myapp', // Reads: myapp.config.ts, .myapprc, package.json#myapp, env
defaults: { port: 3000, debug: false },
});
// Used by Nuxt, Vite, and the UnJS ecosystem for config loading
11. citty — 200K downloads/week
// Modern CLI framework: type-safe, minimal, excellent autocomplete
import { defineCommand, runMain } from 'citty';
const main = defineCommand({
meta: { name: 'my-cli', description: 'A CLI tool' },
args: {
input: { type: 'string', required: true },
verbose: { type: 'boolean', default: false },
},
run({ args }) {
console.log(args.input, args.verbose);
},
});
runMain(main);
12. listhen — 200K downloads/week
// Create a local server with automatic port finding and ngrok-style tunnels
import { listen } from 'listhen';
const listener = await listen(app, {
port: 3000, // Tries 3000, then 3001, etc. if taken
public: true, // Optional: expose via tunnel
});
console.log(listener.url); // http://localhost:3000 or tunnel URL
13. hookable — 500K downloads/week
// Type-safe hook system (plugin/extension pattern)
import { createHooks } from 'hookable';
const hooks = createHooks<{
beforeRequest: (req: Request) => void;
afterResponse: (res: Response) => void;
}>();
hooks.hook('beforeRequest', (req) => {
req.headers.set('Authorization', `Bearer ${token}`);
});
// Used by Nuxt's entire extension system
14. unstorage — 600K downloads/week
// Universal key-value storage: same API for memory, filesystem, Redis, KV
import { createStorage } from 'unstorage';
import lruCacheDriver from 'unstorage/drivers/lru-cache';
import redisDriver from 'unstorage/drivers/redis';
const storage = createStorage({
driver: process.env.NODE_ENV === 'production'
? redisDriver({ url: process.env.REDIS_URL })
: lruCacheDriver({ max: 500 }),
});
await storage.setItem('user:123', { name: 'Royce' });
await storage.getItem('user:123'); // { name: 'Royce' }
React / Frontend
15. use-immer — 450K downloads/week
// Immer-powered useState: mutate state immutably
import { useImmer } from 'use-immer';
const [draft, updateDraft] = useImmer({ items: [], count: 0 });
// Instead of: setDraft(prev => ({ ...prev, items: [...prev.items, newItem], count: prev.count + 1 }))
updateDraft(draft => {
draft.items.push(newItem);
draft.count++;
}); // Much cleaner for nested state updates
16. react-aria — 1.4M downloads/week
// Adobe's headless accessible component primitives
import { useButton, useFocusRing } from 'react-aria';
// Foundation for building WCAG-compliant components
// Used by: React Aria Components (full component library built on top)
// Different from shadcn: more low-level, maximum customization
17. wouter — 500K downloads/week
// React Router alternative: 2.4KB, hooks-first, same API
import { Route, Link, useLocation } from 'wouter';
// Drop-in replacement for most React Router use cases
// Perfect for: admin panels, SPAs that don't need complex routing
18. sonner — 800K downloads/week
// Toast notifications: opinionated, beautiful, small
import { toast, Toaster } from 'sonner';
// <Toaster /> in layout
toast.promise(saveUser(), {
loading: 'Saving...',
success: 'Saved!',
error: (err) => `Error: ${err.message}`,
});
Build / Dev Tools
19. tsup — 1.8M downloads/week (underrated for its category)
// Bundle TypeScript libraries for npm: generates CJS, ESM, types
// tsup.config.ts
import { defineConfig } from 'tsup';
export default defineConfig({
entry: ['src/index.ts'],
format: ['cjs', 'esm'],
dts: true, // Generate .d.ts files
sourcemap: true,
clean: true,
});
// Run: npx tsup
// Replaces: complex rollup/webpack config for library authors
20. changelogen — 150K downloads/week
// Changelog generation from conventional commits
// npx changelogen --release
// Generates CHANGELOG.md, bumps version, creates git tag
// Follows the same format as Vite/Nuxt changelogs
Testing
21. @mswjs/data — 300K downloads/week
// Model-based API mocking: define data models, mock query/filter/sort
import { factory, primaryKey } from '@mswjs/data';
const db = factory({
user: { id: primaryKey(String), name: String, age: Number },
});
db.user.create({ id: '1', name: 'Alice', age: 28 });
db.user.findMany({ where: { age: { gte: 18 } } });
// Pairs with MSW for realistic API mocking in tests
Honorable Mentions (Brief)
// More packages worth knowing:
// - httpxy: zero-dep HTTP proxy middleware
// - rou3: Radix router for Node.js (blazing fast route matching)
// - h3: Minimal HTTP server (Nuxt/Nitro's foundation)
// - magic-string: sourcemap-aware string manipulation (used by Rollup)
// - acorn: JavaScript AST parser (used by all bundlers)
// - estree-walker: walk/transform ESTree-format ASTs
// - kolorist: tiny terminal coloring (like picocolors, 0.5KB)
// - mlly: ES module utilities for Node.js
// - std-env: detect runtime environment (Node/Bun/Deno/Edge/browser)
// - consola: Beautiful console logging with levels (used by Nuxt)
// - unenv: Universal environment compatibility (platform-agnostic code)
// - tinycolor2: Color manipulation, 0 deps
// - culori: Color science library, zero-dep
// - fuzzysort: Fast fuzzy search (Sublime Text algorithm in JS)
// - meilisearch: Full-text search client, better Algolia alternative
// - lottie-web: JSON animation library
// - floating-ui: Tooltips/popovers positioning (Radix uses this)
// - vaul: Drawer component for React
// - cmdk: Command palette for React (shadcn uses this)
// - input-otp: OTP input field for React
Compare health scores and download trends for any npm package at PkgPulse.
See the live comparison
View lodash vs. underscore on PkgPulse →