fuzzy
fuzzy is a tiny CommonJS utility for subsequence matching. Give it a short pattern such as `bcn` and a list of strings, and it keeps entries whose characters appear in that order, scores consecutive matches more highly, and can wrap the matched characters for display. It works in Node and through a direct browser script, but it is a simple filter and highlighter, not typo correction, token search, indexing, or natural-language search.
Install it only for a tiny legacy-friendly subsequence filter whose limitations are acceptable. Fuse.js or fuzzysort is a better default when ranking quality, multi-field data, TypeScript ergonomics, or active maintenance matters.
Use it if
- You need a very small, dependency-free subsequence filter for a command palette or short in-memory list
- You want match positions rendered with configurable prefix and suffix strings without writing a highlighter
- Your data is objects and a synchronous extract function can select the one string to search
- You maintain CommonJS or direct-script code and do not need a modern module build
- You need typo tolerance or edit-distance matching: the implementation only accepts pattern characters found in order, so transpositions and missing characters are not corrected
- You need the best occurrence inside a string: the README TODO admits it takes the first subsequence, so `bass` can highlight scattered letters in `bodacious bass` instead of the obvious word
- You search large or frequently changing collections in a UI: filtering and sorting are synchronous, and the README still lists asynchronous batches or Web Workers as unfinished work
- You want current ESM packaging and actively maintained TypeScript declarations: version 0.1.3 was published in 2016, exports only CommonJS, and ships a minimal hand-written declaration file
- You need multi-field ranking: `extract` returns one string per item, while the README lists matching across multiple object strings as an unimplemented feature
Setup reality
Installation is only `npm install fuzzy`; there are no runtime dependencies, native builds, peer dependencies, credentials, or configuration files. The friction is age and API shape. The package exports CommonJS, so native ESM projects normally use a default import backed by Node interoperability, while older TypeScript settings may require `import fuzzy = require('fuzzy')`. A declaration file is included, but it models the small 2016 API rather than providing modern generics and rich option types. Matching is synchronous and scans every candidate, then `filter` sorts every match by score, so put debouncing or list limits around browser search boxes yourself. Highlight output is created by concatenating the `pre` and `post` strings into the original text. If those wrappers are HTML, render only trusted source strings or escape them first; the library does no sanitization. Matching is case-insensitive unless `caseSensitive: true`, accepts only one extracted string per object, and returns result objects rather than the original array. Empty patterns match everything, a non-string pattern passed to `filter` returns the input array itself, and a failed `match` returns `null`. Those details are easy to miss because the README is the complete documentation and the last release predates current Node module conventions.
Patterns
Filter and rank a list of stringsfilter-strings
const fuzzy = require('fuzzy');
const results = fuzzy.filter('bcn', [
'baconing',
'narwhal',
'a mighty bear canoe',
]);
const matches = results.map((result) => result.original);Results are sorted by descending score and contain `string`, `score`, `index`, and `original`; they are not the original values directly.
Check whether one string matchestest-one-string
const matches = fuzzy.test('bcn', 'a mighty bear canoe');
console.log(matches); // trueThis is ordered subsequence matching: all pattern characters must appear in order, but they do not need to be adjacent.
Read one match scoreinspect-match
const match = fuzzy.match('abc', 'a-b-c');
if (match) {
console.log(match.rendered); // a-b-c
console.log(match.score);
}`match` returns `null` when the pattern fails; exact matches receive `Infinity` as their score.
Wrap matching charactershighlight-match
const result = fuzzy.match('bcn', 'baconing', {
pre: '<mark>',
post: '</mark>',
});
console.log(result?.rendered);The returned string is not sanitized. Escape untrusted candidate text before inserting HTML wrappers and rendering it.
Search objects through one fieldfilter-objects
const commands = [
{ id: 'open', label: 'Open file' },
{ id: 'close', label: 'Close editor' },
];
const results = fuzzy.filter('of', commands, {
extract: (command) => command.label,
});
console.log(results[0].original.id);`extract` must return one string. The library cannot assign weights to several fields or report which field matched.
Require matching letter casecase-sensitive-match
const exactCase = fuzzy.match('Ab', 'Abacus', {
caseSensitive: true,
});
const wrongCase = fuzzy.match('Ab', 'abacus', {
caseSensitive: true,
}); // nullMatching is case-insensitive by default; set `caseSensitive` on `match` or `filter` when case carries meaning.
Return only matching stringssimple-filter
const values = fuzzy.simpleFilter('ab', [
'alphabet',
'cab',
'clock',
]);
console.log(values); // ['alphabet', 'cab']`simpleFilter` preserves input order and discards scores and highlighting; use `filter` when ranking matters.
Limit work shown by an autocompletelimit-results
function searchCommands(query, commands) {
if (!query) return commands.slice(0, 20);
return fuzzy
.filter(query, commands, { extract: (item) => item.label })
.slice(0, 20)
.map((result) => result.original);
}The limit is applied after the package filters and sorts the whole list, so it limits rendering but not matching cost.
Debounce synchronous browser filteringdebounce-browser-search
let timer;
input.addEventListener('input', (event) => {
clearTimeout(timer);
timer = setTimeout(() => {
const results = fuzzy.filter(event.target.value, labels);
render(results.slice(0, 25));
}, 100);
});The package has no asynchronous or worker API. Debouncing reduces repeated calls but a large single search can still block the main thread.
Use the CommonJS package from Node ESMesm-import
import fuzzy from 'fuzzy';
const found = fuzzy.filter('cfg', ['config.js', 'package.json']);There is no native ESM export. This default import relies on Node's CommonJS interoperability and may need bundler-specific interop settings.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| fuzzysort | npm | You want fast fuzzy ranking, match indexes, and a browser-oriented API for larger candidate sets |
| fuse.js | npm | You need typo tolerance, weighted multi-key object search, thresholds, and a maintained search index |
| fast-fuzzy | npm | You want a maintained TypeScript-friendly matcher with configurable normalization and sorting |