Fuse.js is a lightweight, zero-dependency fuzzy-search library written in TypeScript. It works in the browser and on the server, and is designed for searching small-to-medium datasets on the client side where you can't rely on a dedicated search backend.
Multi-word fuzzy search with relevance ranking. Type "javascrpt paterns" and find "JavaScript Patterns" — typo tolerance, multiple words, and smart ranking all at once.
const fuse = new Fuse(docs, {
useTokenSearch: true,
keys: ['title', 'author', 'description']
})
fuse.search('javascrpt paterns')
// → [{ item: { title: 'JavaScript Patterns', ... } }]See Token Search below for details.
Search large datasets without freezing the UI. FuseWorker splits your data across multiple Web Workers and searches in parallel — ~5x faster on 100K documents.
import { FuseWorker } from 'fuse.js/worker'
const fuse = new FuseWorker(docs, {
keys: ['title', 'author', 'description']
})
const results = await fuse.search('query')
fuse.terminate()Same options and results as Fuse — just async. Function-valued options (sortFn, getFn, keys[].getFn) aren't supported because functions can't be transferred to a worker; everything else carries over. See the Web Workers docs for the interactive demo and full API.
npm install fuse.jsyarn add fuse.jsOr include directly via CDN:
<script src="https://cdn.jsdelivr.net/npm/fuse.js/dist/fuse.min.mjs"></script>import Fuse from 'fuse.js'
const books = [
{ title: "Old Man's War", author: 'John Scalzi' },
{ title: 'The Lock Artist', author: 'Steve Hamilton' },
{ title: 'HTML5', author: 'Remy Sharp' },
{ title: 'JavaScript: The Good Parts', author: 'Douglas Crockford' }
]
const fuse = new Fuse(books, {
keys: ['title', 'author']
})
fuse.search('javscript')
// → [{ item: { title: 'JavaScript: The Good Parts', ... }, ... }]The core of Fuse.js. Uses the Bitap algorithm for approximate string matching — handles typos, misspellings, and partial matches out of the box.
fuse.search('javscript')
// → [{ item: { title: 'JavaScript: The Good Parts', author: 'Douglas Crockford' } }]Search across multiple fields with different importance levels. Title matches can rank higher than description matches.
const fuse = new Fuse(docs, {
keys: [
{ name: 'title', weight: 2 },
{ name: 'description', weight: 1 }
]
})Use operators for precise control: exact match (=), prefix (^), suffix (!), and more. Enable with useExtendedSearch: true.
const fuse = new Fuse(list, {
useExtendedSearch: true,
keys: ['title']
})
fuse.search('=exact match') // exact match
fuse.search('^prefix') // starts with
fuse.search('!term') // does not includePrefer a structured, self-documenting alternative to the magic characters? Express the same operators as an object. Unlike the string form, this needs no useExtendedSearch flag (the operators are unambiguous), autocompletes in TypeScript, and needs no quoting or escaping.
const fuse = new Fuse(list, { keys: ['title', 'author'] })
fuse.search({ title: { $startsWith: 'old' }, author: { $eq: 'Kay' } })Each operator maps to a string equivalent:
| Object | String | Matches when the field |
|---|---|---|
$fuzzy |
term |
fuzzy-matches (typo tolerant) |
$eq |
=term |
equals the value |
$contains |
'term |
contains the value |
$startsWith |
^term |
starts with the value |
$endsWith |
term$ |
ends with the value |
Negate with $not, which wraps exactly one of $contains, $startsWith, or $endsWith:
fuse.search({ title: { $not: { $contains: 'draft' } } }) // === '!draft'
fuse.search({ title: { $not: { $startsWith: 'old' } } }) // === '!^old'
fuse.search({ title: { $not: { $endsWith: '.go' } } }) // === '!.go$'Multiple operators on one field are AND-ed; use field-local $and / $or for more, and compose across fields with logical search:
// starts with "old" AND does not contain "draft"
fuse.search({ title: { $startsWith: 'old', $not: { $contains: 'draft' } } })
// title starts with "old" OR ends with "war"
fuse.search({ title: { $or: [{ $startsWith: 'old' }, { $endsWith: 'war' }] } })Object queries return identical results to the equivalent string query, and validate strictly: unknown operators, empty or non-string values, and illegal nesting throw instead of silently degrading to a fuzzy search. Available in the full build. See the Extended Search docs for the full grammar.
Splits multi-word queries into individual terms, fuzzy-matches each independently, and ranks results using BM25-style IDF weighting. Enable with useTokenSearch: true.
const fuse = new Fuse(docs, {
useTokenSearch: true,
keys: ['title', 'body']
})
fuse.search('express midleware rout')
// Finds "Express Middleware" and "Express Routing Guide" despite typos- Typo tolerance per word — each term is fuzzy-matched independently
- Relevance ranking — rare terms are weighted higher than common ones
- Word order independent —
"patterns javascript"and"javascript patterns"return identical results - No query length limit — long multi-word queries work naturally since each term is searched separately
- AND or OR —
tokenMatch: 'all'returns only records matching every word (filtering); the default'any'matches any word - Custom tokenizer — pass a regex or function via
tokenizefor tokens with internal punctuation (node.js,c++), or useIntl.Segmenterfor CJK / Thai word segmentation. Unicode-aware by default
Available in the full build. See the Token Search docs for details and performance benchmarks.
Combine conditions with $and and $or for complex queries. Available in the full build.
fuse.search({
$and: [{ title: 'javascript' }, { author: 'crockford' }]
})Get character-level match indices for highlighting search results in your UI.
const fuse = new Fuse(list, {
includeMatches: true,
keys: ['title']
})
const result = fuse.search('javscript')
// result[0].matches[0].indices → [[0, 9]]Use Fuse.match() to fuzzy-match a pattern against a single string without creating an index. Useful for one-off comparisons or custom filtering.
const result = Fuse.match('javscript', 'JavaScript: The Good Parts')
// → { isMatch: true, score: 0.04, indices: [[0, 9]] }Fuse.match() does not support useTokenSearch — token search requires corpus-level statistics (df, fieldCount) that a one-off string comparison can't provide. Passing useTokenSearch: true throws an explicit error. Use new Fuse(docs, { useTokenSearch: true }).search(query) for token-search behavior.
Add and remove documents from a live index without rebuilding.
fuse.add({ title: 'New Book', author: 'New Author' })
fuse.remove((doc) => doc.title === 'Old Book')Fuse.js ships in two variants:
| Build | Includes | Min + gzip |
|---|---|---|
| Full | Fuzzy + Extended + Logical + Token search | ~8.6 kB |
| Basic | Fuzzy search only | ~6.8 kB |
Use the basic build if you only need fuzzy search and want the smallest bundle size.
For the full API reference, configuration options, scoring theory, and interactive demos, visit fusejs.io.
- fuse-swift: Swift port for iOS, macOS, tvOS, watchOS, visionOS, and Linux. Byte-equivalent results, idiomatic Swift API, syncs with each upstream release. Currently in 2.0.0-rc.1.
- Become a backer or sponsor on GitHub
- Become a backer or sponsor on Patreon
- One-time donation via PayPal
See DEVELOPERS.md for setup, scripts, and project structure.
See CONTRIBUTING.md for guidelines on issues and pull requests.