# zxcvbn-ts: Dropbox password strength estimation rewritten in TypeScript

> zxcvbn-ts is a TypeScript rewrite of Dropbox's zxcvbn, the password strength estimator that scores a candidate by guessing it the way a cracking tool would rather than by counting character classes. It ships the core library plus separate language packages for dictionaries and feedback translations, and it adds asynchronous matchers and a haveibeenpwned matcher to the original design. The trade-off is that the scoring is only as good as the dictionary you install, and the rewrite differs slightly from the original.

**zxcvbn-ts/zxcvbn** — Low-Budget Password Strength Estimation

- Repository: https://github.com/zxcvbn-ts/zxcvbn
- Website: https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/wheeler
- Stars: 1,236 · Forks: 96
- Language: TypeScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/zxcvbn-ts-zxcvbn

## What zxcvbn actually measures

zxcvbn is described in the README as a password strength estimator inspired by password crackers. Through pattern matching and conservative estimation, it recognises and weighs 40k common passwords, common names and surnames, popular words from Wikipedia, and common words in different languages from different countries, along with other common patterns such as dates, repeats like aaa, sequences like abcd, keyboard patterns like qwertyuiop, and l33t speak.

That is the whole idea, and it differs from the usual strength meter. A composition rule asks what character classes a password contains, and it is easy to satisfy with a password that a cracking tool tries in the first hundred guesses. zxcvbn instead tries to estimate the number of guesses an attacker would need, and reports a score from zero to four. A password in a common-password list lands at zero no matter how many character classes it contains, and a passphrase of uncommon words lands high.

The README argues the case in three parts, and the argument is the reason to prefer this approach. Policies often fail both ways, allowing weak passwords such as P@ssword1 and disallowing strong ones. The estimator is flexible because many password styles are allowed as long as enough complexity is detected, with passphrases rated highly given enough uncommon words, keyboard patterns ranked by length and number of turns, and capitalisation adding complexity only when it is unpredictable. And it is usable, because the design targets rule-free interfaces with instant feedback, including minimal targeted verbal feedback that can guide a user towards a less guessable password.

The reasoning behind the design is in the USENIX Security 2016 paper and presentation by Wheeler, which the README links and which the repository's homepage field points at.

## Why a rewrite was needed at all

zxcvbn-ts is a complete rewrite of the Dropbox zxcvbn project into TypeScript, and the original is MIT licensed, which is what allowed the rewrite in the first place. The README credits the original creators at Dropbox.

The stated reason for the project is to modernize zxcvbn and make it maintainable with new features. That is a smaller claim than it sounds. The practical question for any adopter is whether they want to depend on the original artefact or on a maintained one with a typed API.

The differences that matter in practice are visible in the feature list. The rewrite is written in TypeScript, so it ships types, which matters for a library that has to sit behind a form validation schema. It supports i18n for both dictionaries and feedback translations, so a password in a language other than English can be scored against the right word list and the feedback can be returned in the user's language. It allows existing dictionaries to be extended with your own, so a product with its own internal naming scheme can add those terms to the list.

One feature deserves particular attention because it changes the shape of the API. The library can check passwords asynchronously for API-based matchers, and it includes a haveibeenpwned matcher. A breach check needs a network call, so this is a structural addition rather than a wrapper.

## The core library, the language packages and the dictionaries

The repository is a monorepo with two workspace globs, packages/languages and packages/libraries, and the published entry point in the README is the @zxcvbn-ts/core package. The language packages are separate npm packages, one per language, and the README tells you where to look if your language is missing.

That separation is the design that matters. The dictionaries are large, and a browser application that only needs to score German passwords should not ship the English, Chinese and Japanese word lists. Installing a language pack brings its own common password list, its names and surnames, its Wikipedia-derived words and its translation of the feedback strings, and the core library does the matching and scoring against whatever dictionary set is loaded.

The guide for adding a new language package is linked from the documentation, so the path for a missing language is documented rather than improvised. That matters for anyone whose user base is not covered, since a wrong-language dictionary quietly produces wrong scores: a password in a script the dictionary does not cover will look unusual, and therefore strong, to the estimator.

The README is also candid about the edge case. The library is usable without dictionaries at all, which reduces the scoring efficiency rapidly, and it says plainly that this is not recommended. That is a warning worth taking seriously, because a dictionary-less installation is not a weaker version of the estimator, it is a different algorithm that only sees sequences, repeats and keyboard patterns, and it will rate a password made of a common word as strong.

## Matchers, asynchronous checks and the haveibeenpwned integration

The feature list names the extension points: a custom matcher, asynchronous checking for API-based matchers, and a haveibeenpwned matcher.

A matcher is how the library decides that a candidate is a known pattern. The built-in matchers cover the common password list, the dictionary words, dates, repeats, sequences, keyboard walks and l33t substitutions, and a custom matcher lets you add a rule of your own, for example a company specific naming convention or an internal identifier format. Adding a matcher changes the score, because a match that is recognised is a match the cracker would also find.

The haveibeenpwned matcher changes the shape of the call. The client still has to make a network request and wait. The asynchronous API exists for that reason, and a form that scores a password should call it after the user stops typing rather than on every keystroke.

The combination matters for the feedback design the README describes. A purely local score tells a user how guessable a password is in general. A local score plus a breach check tells a user whether the exact password they picked is already in a breach corpus, which is a much more actionable piece of advice and one no local algorithm can give.

## Installing the core package and the workspace that produces it

The published package is @zxcvbn-ts/core, and the README badges point at exactly that name on npm, which is the package to install for the estimator itself.

You also need the language package for the languages your users type in, installed alongside the core, and the documentation site at zxcvbn-ts.github.io/zxcvbn carries the API reference, a demo and a migration guide for people moving from the original library.

Behind the published packages is a fairly heavy workspace. The root package.json declares yarn workspaces over the two package directories, and the build script runs rollup through lerna in parallel across every package.

```json
"workspaces": [
 "packages/languages/*",
 "packages/libraries/*"
],
"build": "lerna exec --parallel -- rollup -c../../../scripts/rollup.config.mjs",
"test": "jest --coverage",
"typecheck": "tsc --noEmit"
```

The release process is conventional-commits driven, with lerna publish, and there is a separate beta path that publishes a premajor version under the beta dist-tag. A type-level test script runs arethetypeswrong against every packed package, which is how the team checks that the emitted type declarations work in every consumer module configuration rather than only in the one the build produced.

The dictionary data has its own generation pipeline in data-scripts/. There is a language data generator, a deduplication script, and a Wikipedia extractor that pulls the source material for the word lists. The docs are built with vuepress, and the root also carries a turbo.json, an eslint config, a prettier module, a jest config and a.versionrc for version bumps.

## Where zxcvbn-ts is the wrong tool

The first limitation is that it does not measure a breach. A strong score means the password would take an attacker a lot of guesses against the estimator's model, which is not the same as being a password nobody has ever used. The haveibeenpwned matcher closes that gap, but only if you opt into it and only if the user is on a network that reaches the service.

The second limitation is the score itself. Four grades and a feedback string is a coarse instrument, and a product that wants to display entropy in bits has to build that on top, because the library reports an ordinal score rather than a guess count.

The third limitation is the language coverage. The dictionaries are per language, and if your users pick a password in a language that has no pack, the estimator will not recognise the common words in it and will rate it higher than it deserves. This is a quiet failure, and the mitigation is to add a language pack, which is documented work rather than a configuration flag.

The fourth limitation is the divergence from the original. The documentation includes a comparison page for exactly this reason, so that people moving from Dropbox zxcvbn know where the scoring changed. A rewritten algorithm that scores differently from the version your security policy was calibrated against is a real migration cost, and one that shows up as a support question rather than as an error.

The fifth limitation is release hygiene from the consumer's side. The repository has no GitHub releases, so the version comes from the published npm packages, and the root package.json version of 4.4.2 reflects the workspace rather than any single library.

## zxcvbn-ts next to the original zxcvbn and haveibeenpwned

The comparison with the original Dropbox zxcvbn is the one that matters most, and the rewrite's own answer is the comparison page.

The deciding factor between the two is maintenance rather than capability. The rewrite is typed, packaged as separate core and language modules, supports asynchronous matching, and is still being worked on, with the last push on 2026-10-09. Against that, the original carries the reference implementation and the reference scores, and the paper that explains the method. For a project that wants the original behaviour exactly, with no scored differences, staying on the original is defensible.

haveibeenpwned is a service, not an estimator, and the two answer different questions. It answers whether a specific password appears in a corpus of breached passwords, which is a yes or no question about this exact string. zxcvbn answers how expensive this string is to guess, which is a graded question about any string. A product can use either alone, but the sensible pairing is the local estimator for instant feedback and the breach check for the final warning, and zxcvbn-ts provides the matcher for the second part.

The third comparison is with plain length rules and character class rules, which is the approach zxcvbn was written to replace. The README is explicit that composition policy fails in both directions, and the difference is measurable: a rule set admits P@ssword1, and the estimator scores it near zero because it is a common password with a substitution applied.

## Conclusion

Adopt zxcvbn-ts if you are building a TypeScript signup or password change form and want a score that reflects real cracking cost instead of a composition rule, and if you are prepared to install the language pack for your users. Skip it if a breach check plus a length rule is enough for your threat model. Before switching from the original zxcvbn, read the comparison page in the documentation, because the rewrite scores some passwords differently, and confirm the scoring does not drop by more than one grade for your longest common passwords.

## FAQ

### What is zxcvbn-ts and how does it differ from Dropbox zxcvbn?

It is a complete TypeScript rewrite of the Dropbox zxcvbn password strength estimator, kept MIT licensed, with the stated goal of modernizing the library and making it maintainable with new features such as typed APIs, i18n and asynchronous matchers.

### How do I install zxcvbn-ts?

Install the core package @zxcvbn-ts/core from npm, and add the language package for each language your users type in, since the dictionaries and the feedback translations are shipped per language.

### Does zxcvbn-ts check whether a password has been breached?

It includes a haveibeenpwned matcher, and the library supports checking passwords asynchronously for API-based matchers, which is how a network call is kept out of the synchronous scoring path.

### Why does zxcvbn-ts need language packages?

The dictionaries carry the common passwords, names and words for a language, and the feedback strings are translated per language as well, so the core library only sees the language packs you install and rates a password in an uncovered language too highly.

### Can I add my own words to the dictionaries?

Yes, the feature list states that existing dictionaries can be extended with your own, and a custom matcher can be registered to recognise patterns the built-in matchers do not cover.

## Sources

- [Issues](https://github.com/zxcvbn-ts/zxcvbn/issues)
- [License: MIT](https://github.com/zxcvbn-ts/zxcvbn/blob/master/LICENSE)
- [Project website](https://www.usenix.org/conference/usenixsecurity16/technical-sessions/presentation/wheeler)
- [README](https://github.com/zxcvbn-ts/zxcvbn/blob/master/README.md)
- [zxcvbn-ts/zxcvbn on GitHub](https://github.com/zxcvbn-ts/zxcvbn)

---

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