# leven, a small JavaScript implementation of the Levenshtein distance

> sindresorhus/leven is a single-function package that computes the edit distance between two strings, with a maxDistance option that caps the work for untrusted input, and a closestMatch helper that picks the nearest candidate from a list. It ships as an ES module with TypeScript types and no dependencies.

**sindresorhus/leven** — Measure the difference between two strings with the fastest JS implementation of the Levenshtein distance algorithm

- Repository: https://github.com/sindresorhus/leven
- Stars: 735 · Forks: 34
- Language: JavaScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/sindresorhus-leven

## What the package does and how small it is

leven measures the difference between two strings using the Levenshtein distance algorithm, and the repository description calls it the fastest JavaScript implementation of that algorithm.

The package is deliberately tiny. The published files list contains exactly two entries, index.js and index.d.ts, and the package declares sideEffects as false, which means a bundler can drop it entirely if nothing uses it. There are no dependencies at all, and the type field is module with the exports map pointing at index.js, so it is a pure ES module package with TypeScript types shipped alongside.

The engine requirement is worth noting because it is more permissive than the current Node release cycle. The engines field asks for Node 12.20.0 or 14.13.1 or 16.0.0 and above, which is a wide range that means the package can be consumed by older toolchains and by bundler-based builds that never touch Node directly.

Installation is the usual one-liner:

```sh
npm install leven
```

and the usage is one import and one call:

```js
import leven from 'leven';

leven('cat', 'cow');
//=> 2
```

Two edits turn cat into cow, which is the expected distance: replace the a with an o and the t with a w.

The project is by Sindre Sorhus, the licence is MIT, and the release history shows v4.0.0 in August 2021, v4.1.0 in September 2025 and v4.2.0 in September 2026, with the last push on 2026-09-26. A four year gap between two minor releases followed by two more in a year is the pattern of a small package that is stable rather than actively developed, which is what you would expect of a well-solved algorithm with a maintainer who keeps the tooling current.

## The maxDistance option and why it is the important part

The option that matters is maxDistance, and the README explains both what it does and why you should use it.

It caps the distance the function will calculate. If the actual distance exceeds the value, the function returns maxDistance instead of the real distance, and the README says this can significantly improve performance when you only care about matches within a certain threshold.

The example shows both behaviours:

```js
import leven from 'leven';

leven('abcdef', '123456', {maxDistance: 3});
//=> 3

leven('cat', 'cow', {maxDistance: 5});
//=> 2
```

The first pair differs in every position, so the real distance is 6 and the capped result is 3. The second pair has a real distance of 2, which is below the cap, so the true value comes through.

There is a tip in the README that is the most practically useful sentence in the whole document. Without maxDistance, the time grows with the product of the string lengths, so two long strings can block for seconds. With it, the time grows only with the string length times maxDistance. Set it for untrusted input.

The complexity difference is the point. The plain distance requires a full dynamic programming table of the two lengths multiplied, so a pair of ten thousand character strings is a hundred million cells. With a threshold, the algorithm only has to explore the band of the table within maxDistance of the diagonal, which is the standard banded approach and reduces the work to the string length times the threshold.

There is one edge case in the implementation that the README documents and you should know about. Negative and non-integer values of maxDistance are ignored, and the distance is then not capped. So a maxDistance of 0 or of 2.5 behaves as though no cap were set, which is worth knowing because zero looks like it might mean an exact match requirement when it does not.

For any code path where the strings come from outside your control, passing maxDistance is not an optimisation, it is what keeps the function from being a denial of service vector against your own service.

## closestMatch and the candidate list use case

The second export is closestMatch, which finds the closest matching string from an array of candidates.

```js
import {closestMatch} from 'leven';

closestMatch('kitten', ['sitting', 'kitchen', 'mittens']);
//=> 'kitchen'

closestMatch('hello', ['jello', 'yellow', 'bellow'], {maxDistance: 2});
//=> 'jello'

// No match within distance threshold
closestMatch('abcdef', ['123456', '1234567890'], {maxDistance: 2});
//=> undefined
```

The first example is the interesting one because it is not the closest by edit count alone in a naive sense. kitten against kitchen differs in the first character, and against mittens in the first and second, and against sitting in two positions, so kitchen wins with a distance of 1.

The second example shows the threshold doing real work. The candidate bellow differs from hello in the first character, so it has a distance of 1, which is within the threshold of 2. But yellow is at a distance of 2 and jello at 1, so the answer is jello, and the return is the closest rather than the first acceptable match, which is the behaviour that makes the function useful for correction and completion lists.

The third example shows the failure mode. With a threshold of 2, neither candidate is within it, so the function returns undefined rather than a poor match. That is the right behaviour for a suggestion interface, where returning a wildly wrong candidate is worse than returning nothing.

The options are the same as for the distance function, and the maxDistance for closestMatch is the maximum distance to consider, with candidates further away than that being ignored entirely. That means a long candidate list is cheap, because the expensive part of the computation is bounded per candidate rather than by the length of the list.

The related project section points at leven-cli, a command line version of the same module, which is a reminder that the library is small enough that a wrapper around it is a sensible thing to publish.

## The benchmark setup and how this compares to the alternatives

The repository is laid out for a library this size, and the top level is worth reading because it says how the project is verified.

There is index.js and index.d.ts, the test file, a bench.js, and an editorconfig, gitattributes, gitignore, an npmrc and a package.json. The package scripts are minimal and telling. The test script is xo followed by node --test, so the linting runs through xo and the tests use the Node built-in test runner rather than a framework. The bench script runs node bench.js. The files field restricts the published package to index.js and index.d.ts.

The xo configuration in the package metadata is the most interesting file for anyone who wants to understand why the code looks the way it does. Three rules are switched off for the source: unicorn/prefer-code-point, no-bitwise, and node-test/no-conditional-assertion. The bitwise rule being off is a direct signal about the implementation, since a fast Levenshtein implementation is very likely to use bitwise operations on character codes to compare two characters at a time rather than a string comparison, and the code point rule being off suggests the code works on code units rather than iterating code points.

The benchmark file is where the speed claim comes from, and the devDependencies list shows what it is compared against. There is fast-levenshtein, ld, levdist, levenshtein, levenshtein-component, levenshtein-edit-distance and natural, which is a substantial set of alternatives in different languages and styles. The benchmark harness is tinybench, and the test framework for the tests is tap. The presence of natural in that list is interesting, since it is a general string comparison library rather than a dedicated distance implementation, so the comparison is against a broader set of approaches rather than only against other Levenshtein packages.

The comparison for a user is straightforward. There are many Levenshtein packages for JavaScript, and the differences that matter are bundle size, whether they are typed, whether they support a distance cap, and raw speed on your data. This package wins on smallness and on the threshold option, and the README's own benchmark file is where you can check the speed claim for your own string lengths rather than taking the description on trust.

One thing to be aware of when reading the numbers is that the fastest implementation depends heavily on the input. Short strings, long strings, strings that share prefixes and strings that differ entirely all favour different strategies, and a package that wins on one is not necessarily the fastest on another. Since the package exposes maxDistance, the practical test is to run the benchmark with the threshold you would actually use.

## Conclusion

Adopt leven if you need fuzzy string matching in Node and want a single dependency free function with TypeScript types. Skip it if you need the distance between very long strings with no threshold, because the README warns that the work grows with the product of the string lengths and can block for seconds. Verify first by passing a maxDistance when you take input from users, and by measuring against the alternatives in the benchmark file before you assume this one is fast enough for your data.

## FAQ

### What does the leven package do?

It measures the difference between two strings using the Levenshtein distance algorithm, with the repository describing it as the fastest JavaScript implementation of that algorithm. It ships as an ES module with TypeScript types and no dependencies.

### What does the maxDistance option do?

It caps the distance that will be calculated, so if the real distance exceeds the value the function returns maxDistance instead. The README says it can significantly improve performance when you only care about matches within a threshold, and that negative and non-integer values are ignored so the distance is then not capped.

### Why should I set maxDistance for untrusted input?

Because without it the running time grows with the product of the two string lengths, so two long strings can block for seconds, while with it the time grows only with the string length times maxDistance. The README explicitly says to set it for untrusted input.

### How does closestMatch work?

It finds the closest matching string from an array of candidates, using the same options as the distance function, and the maxDistance there is the maximum distance to consider with further candidates ignored. It returns undefined if there are no candidates or no match within the threshold.

### How is the leven package tested and benchmarked?

The test script runs xo and then the Node built-in test runner, and the bench script runs node bench.js. The benchmark uses tinybench and compares against fast-levenshtein, ld, levdist, levenshtein, levenshtein-component, levenshtein-edit-distance and natural.

## Sources

- [Issues](https://github.com/sindresorhus/leven/issues)
- [License: MIT](https://github.com/sindresorhus/leven/blob/main/LICENSE)
- [README](https://github.com/sindresorhus/leven/blob/main/README.md)
- [Releases](https://github.com/sindresorhus/leven/releases)
- [sindresorhus/leven on GitHub](https://github.com/sindresorhus/leven)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sindresorhus-leven
