fuzzysort
fuzzysort is a dependency-free JavaScript fuzzy matcher modeled after Sublime Text's file search. Give it a query and strings or objects, and it returns ranked results with scores, matched character indexes, and a highlighting helper. Version 3 uses scores from 0 to 1, supports nested or computed object keys, handles accents and ligatures, and can combine space-separated terms across several fields. It is a compact in-memory matcher, not a full search index or typo-tolerant search service.
A sharp choice for fast command-palette-style matching over a local array, especially when highlighted character ranges matter. Pick Fuse.js or a real search engine if typo tolerance, indexing, or rich text-search behavior matters more than tiny size.
Use it if
- You need command-palette, file-picker, or small catalog search entirely in the browser
- You want matched character positions or highlighted output alongside ranked results
- Your records fit comfortably in memory and you can prepare mostly static targets once for repeated searches
- You want zero runtime dependencies and a roughly 3.2 KB gzipped bundle
- You need typo correction or edit-distance matching: the allowTypo option was removed, so missing, substituted, or transposed letters can fail where Fuse.js would still match
- You are searching a large remote dataset: fuzzysort scans an in-memory array and provides no index persistence, pagination, stemming, language analysis, or server query layer
- You rely on old tutorials: v3 moved highlighting and indexes onto each result and changed scores from negative numbers to a 0-to-1 scale, so v1 and v2 examples are actively misleading
- You need polished reference documentation: the README is the documentation, its advanced scoreFn sample has an easy-to-copy operator-precedence mistake, and there is no separate API site
Setup reality
npm install fuzzysort is the whole dependency setup, and TypeScript declarations ship in the package. The real work is data shaping: choose searchable fields, normalize null values, prepare stable strings when repeated-query performance matters, cap results, and render highlighted output safely. Because matching is synchronous and in memory, large lists may need a worker or a server-side search system to keep the UI responsive.
Patterns
Rank a list of stringssearch-strings
import fuzzysort from 'fuzzysort'
const files = ['src/App.tsx', 'src/api/users.ts', 'README.md']
const results = fuzzysort.go('sau', files)
for (const result of results) {
console.log(result.target, result.score)
}v3 scores run from 0 to 1 and higher is better; old examples using negative scores describe earlier majors.
Search objects by one keysearch-object-key
const commands = [
{ id: 'open', label: 'Open file' },
{ id: 'settings', label: 'Preferences: Open Settings' },
]
const results = fuzzysort.go('opset', commands, { key: 'label' })
console.log(results[0].obj.id)With key, each result's obj points to the original object while target contains the searched string.
Search a computed object valueuse-computed-key
const results = fuzzysort.go('berry item', products, {
key: product => [product.name, ...(product.tags ?? [])].join(' '),
})A key function is useful for arrays and optional fields; return a string rather than undefined.
Match terms across several fieldssearch-multiple-keys
const results = fuzzysort.go('attack berry', products, {
keys: ['title', 'description', product => product.tags?.join(' ') ?? ''],
threshold: 0.4,
limit: 20,
})
const best = results[0]
console.log(best.obj, best.score)With keys, a result is also indexable: best[0], best[1], and best[2] hold the per-key matches.
Boost a preferred recordweight-results
const results = fuzzysort.go('attack berry', products, {
keys: ['title', 'description'],
scoreFn: result => result.score * (result.obj.bookmarked ? 2 : 1),
})Keep the conditional in parentheses. Without them, JavaScript operator precedence can turn the intended numeric boost into a constant.
Highlight matched characters as HTMLhighlight-html
const result = fuzzysort.single('cfg', 'src/config.ts')
const html = result?.highlight('<mark>', '</mark>') ?? ''This returns an HTML string. Do not inject untrusted target text with innerHTML unless it has been escaped or sanitized.
Render highlights as React nodeshighlight-react
function Highlighted({ result }) {
return result.highlight((match, index) => (
<mark key={index}>{match}</mark>
))
}The callback form returns an array of strings and callback values, which React can render without raw HTML injection.
Prepare stable targets for repeated searchesprepare-targets
const prepared = files.map(file => ({
file,
search: fuzzysort.prepare(file),
}))
const matches = fuzzysort.go(query, prepared.map(item => item.search), {
threshold: 0.5,
limit: 50,
})Preparation helps when targets rarely change, but mapping only prepared strings means results no longer carry your original objects.
Prepare fields while retaining recordskeep-object-reference
const preparedFiles = files.map(file => ({
...file,
preparedName: fuzzysort.prepare(file.name),
}))
const results = fuzzysort.go(query, preparedFiles, { key: 'preparedName' })
console.log(results[0]?.obj.path)Store the prepared value on the object and search that key when you need obj to reference the source record.
Choose behavior for an empty queryhandle-empty-query
const results = fuzzysort.go(query, commands, {
key: 'label',
all: true,
limit: 20,
})Empty searches return nothing by default; all: true returns targets, so always combine it with a sensible limit for UI lists.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| fuse.js | npm | You need edit-distance typo tolerance, weighted keys, and a more fully documented search configuration |
| fast-fuzzy | npm | You want a small fuzzy matcher with configurable normalization and a straightforward search function |
| match-sorter | npm | You want predictable ranked filtering for UI lists, especially alongside React Testing Library conventions |