Sometimes you procrastinate one feature by writing another. Even if that other feature is pretty small and inconsequential. 1

For me, that other feature — if you can even call it a feature — was password strength testing for Buttondown.

This is one of those things that I always liked on other sites, but didn’t really know how to implement myself!

Turns out, the answer is “you let another library do all the heavy lifting”. That other library is zxcvbn by the fine folks at Dropbox: you can just pass in a string and it will return analysis on that string, including a strength (ranging from 0 to 4). Then all you have to do is write a little component like this:

  <span class="tiny">
      <pending-indicator :pending="pending" />
      <span v-if="!pending">
        <span class="error" v-if="strength === 0">Please choose a longer password.</span>
        <span class="error" v-if="strength === 1">Please choose a longer password.</span>
        <span class="warning" v-if="strength === 2">Better... but not quite enough.</span>
        <span class="info" v-if="strength === 3">This is a good password.</span>
        <span class="success" v-if="strength === 4">Great!  This is a secure password.</span>

  import _ from 'lodash';
  import zxcvbn from 'zxcvbn';

  export default {
    props: {
      password: String,
      onStrengthUpdate: Function,

    data() {
      return {
        strength: null,
        checkedPassword: String,

    computed: {
      pending() {
        return !!this.password.length && this.checkedPassword !== this.password;

    watch: {
      password: _.debounce(async function () {
        this.strength = zxcvbn(this.password).score;
        this.checkedPassword = this.password;
      }, 500),
      strength(strength) { this.onStrengthUpdate(strength); },

A couple pieces of note:

  • pending-indicator is just a tiny component that returns a spinning icon when pending is true.
  • We debounce the watcher and cache the last-checked password in checkedPassword to avoid re-running the method every time the user types a single character.
  • onStrengthUpdate can be used by the parent component to conditionally disable registration.

And that’s it! It’s super simple. Here it is in action:

The finished product.

  1. Okay, especially if that feature is pretty small and inconsequential. [return]
Liked this post? Follow me!