Password Strength
Honest password strength feedback without a megabyte dictionary.
npm install @devix-labs/password-strength
zxcvbn is 398 kB gzipped and was last published in February 2017; its maintained TypeScript successor comes to 845 kB with the English word lists, because that is where its accuracy lives. Both measured from their own bundles. zxcvbn's own #169 is titled 'Dist size makes this lib unusable in a client'. The other half of the field is worse than useless: the small libraries, and almost every bar you have ever seen, count character classes — which gives P@ssw0rd1 full marks and marks correct horse battery staple down, exactly backwards. This is 8.4 kB and scores the things that actually occur: the common-password list, l33t undone before the list is consulted, keyboard runs including on an Arabic layout, sequences, repeats, dates, years, and whatever the person has already typed into your other fields. The advice ships in English, Arabic and Urdu — zxcvbn's three most-reacted open issues are all asking for exactly that, 58 reactions between them. An optional breach check sends only the first five characters of the password's SHA-1, and a screen reader hears the band change rather than every keystroke.
What you get
8.4 kB against a measured 398
zxcvbn's published bundle, gzipped on the same machine on the same day. Its successor with English word lists is 845 kB. Its own #169 says the size makes it unusable in a client, and it cannot be fixed without changing what the library is.
No credit for theatre
P@ssw0rd1 scores 0 and correct horse battery staple scores 4. Every bar that counts capitals, digits and symbols gets both of those backwards, and pushing somebody towards the first is worse than showing no meter at all.
The advice is in their language
English, Arabic and Urdu out of the box, and your own pack in four lines. zxcvbn's three most-reacted open issues are all this — #171, #154 and #118, 58 reactions between them — and its feedback is still hardcoded English.
It knows the Arabic keyboard
ضصثقفغ is a straight run along the top row of an Arabic layout — exactly as lazy as qwerty, and invisible to every library that only knows QWERTY. AZERTY and QWERTZ are in there too.
It uses what they already told you
Pass the other fields on your form. Their name or email in the password is a guess an attacker gets for free, and an address counts as several separate things — ahmed, example and the whole of it are all checked.
A breach check that sends no password
Opt in, and the SHA-1 is taken in the browser with only its first five characters sent; the service returns five hundred hashes and the comparison happens on your page. It is the same range API Laravel's Password::uncompromised() uses, so the browser and the server agree.
Two crack times, because there are two attacks
Ten thousand guesses a second against a rate-limited login, and ten billion against a stolen hash. They differ by a million, and quoting one of them is how people end up believing holiday1998 is fine — years online, minutes offline.
A screen reader is not read a bar chart
The bar is a real progressbar with aria-valuetext, and the live region speaks when the band changes rather than on every keystroke — under five announcements for a 27-character passphrase instead of 27, with a test that counts them.
Password Strength — overview
The numbers
zxcvbn is 398 kB gzipped and was last published in February 2017. Its
maintained TypeScript successor, @zxcvbn-ts, comes to 845 kB with the
English word lists, because that is where its accuracy lives. Both measured from
their own published bundles.
Between them they take 2.1 million downloads a week. zxcvbn's #169, "Dist size (JavaScript) makes this lib unusable in a client", has been open for years and cannot be fixed without changing what the library is.
This is 8.4 kB.
The other half of the field is worse than nothing
The small libraries, and almost every bar you have ever seen, score by counting character classes. That measures nothing at all:
P@ssw0rd1— a capital, a digit, a symbol, nine characters. Full marks everywhere. In every wordlist. Cracked instantly.correct horse battery staple— no capitals, no digits, no symbols. Scores badly everywhere. Not cracked.
A meter that pushes people towards the first is worse than no meter. NIST SP 800-63B has said since 2017: stop imposing composition rules, check against known-bad passwords, and let length do the work.
Localisation, which is the top three issues on the incumbent
zxcvbn #171 (+30) "Localization of feedback", #154 (+13) "Feature Request: Localization", #118 (+15) "custom language packs on suggestions". Fifty-eight reactions asking for one thing, and its feedback is still hardcoded English.
Telling somebody in Karachi "Add another word or two" in English tells them nothing. English, Arabic and Urdu ship here, and your own pack is four lines.
The Arabic keyboard
ضصثقفغ is a straight run along the top row of an Arabic layout — exactly as
lazy as qwerty, and invisible to every library that only knows QWERTY. It is
in the patterns here, along with AZERTY and QWERTZ.
A breach check that sends no password
No estimator can tell you whether a password is already public. Checking is the one control NIST actually requires.
The SHA-1 is taken in the browser, the first five hex characters are sent, and
the API returns the ~500 hashes beginning with them; the comparison happens
locally. The service cannot tell which of the five hundred was asked about. It is
the same range API Laravel's Password::uncompromised() uses, so the browser and
your server agree.
Quiet with a screen reader
The bar is a real progressbar with aria-valuetext reading the band as a
word. The live region speaks when the band changes, not when the score does —
a twenty-seven character passphrase produces fewer than five announcements
rather than twenty-seven, and a test counts them.
Colour is never the only signal.
What we do not claim
That this is more accurate than zxcvbn. It is not and cannot be: their accuracy is 398 kB of dictionaries and ours is not. Give it an unusual password built from uncommon English words and zxcvbn will judge it better than we do.
What we claim is that it is forty-seven times smaller, that it catches the cases
that actually occur, that its advice is in the user's language, and that a meter
which never ships because it weighs 398 kB protects nobody. If you can afford
the bytes and want the best estimate available, use @zxcvbn-ts — it is
excellent and it is maintained.
And that a meter is not a security control. It is advice shown while somebody chooses. The controls that work are a minimum length, a breach check, rate limiting, and not forcing rotation — all on the server.
How it compares
Questions
Is this more accurate than zxcvbn?
No, and it cannot be — their accuracy is 398 kB of dictionaries and ours is not. Give it an unusual password built from uncommon English words and zxcvbn will judge it better than we do. What is claimed here is that it is forty-seven times smaller, that it catches the cases that actually occur, that its advice is in the user's language, and that a meter which never ships because it weighs 398 kB protects nobody. If you can afford the bytes, @zxcvbn-ts is excellent and maintained.
Why does P@ssw0rd1 score zero?
Because it is in every wordlist. It has a capital, a digit, a symbol and nine characters, which is what makes every other bar give it full marks — and it is cracked instantly. Replacing letters with lookalike numbers is the first transformation any cracking tool applies, so it buys nothing at all.
Does the password leave the browser?
No. The breach check is off by default, and when it is on, the SHA-1 is computed locally and only its first five hex characters are sent. The service returns every hash beginning with those five — about five hundred — and the comparison happens on your page, so it cannot tell which one you asked about. Point the endpoint at your own proxy if you would rather nothing left your origin.
Can I use the score to block a signup?
You can, but a meter is advice shown while somebody chooses, not a security control, and anything running in a browser can be skipped. The controls that work are on the server: a minimum length, a breach check, rate limiting, and not forcing rotation. That is NIST SP 800-63B, and Laravel's Password rule already does it.
Can I add my own words?
Yes — dictionary takes your brand, your product names, your city, anything your users would reach for. They are then treated exactly like the built-in list. userInputs is the per-user version of the same idea.
More from Devix
All resources →Phone Input
UI component
International phone field with as-you-type formatting, validation and lazy per-country metadata.
Date Picker
UI component
Timezone-safe date picker with Hijri calendars, natural-language typing and a mobile sheet.
Date Range Picker
UI component
Ranges with presets, compare periods and multi-month views.