zxcvbn-ts
Guide
Demo
Changelog
GitHub
Guide
Demo
Changelog
GitHub
  • Guide

    • Introduction
    • Getting started
    • Examples
    • Best Practices
    • Comparison
    • Languages
    • Filtering custom words
    • Lazy loading
    • Matcher
    • Options
    • Framework examples
    • Migration
  • Demo

    • Demo

Framework examples

Those examples are using the full feature set of zxcvbn, and are marked with recommended and optional

Vue

Use a plugin to only update the options once

import { ZxcvbnFactory } from '@zxcvbn-ts/core'
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en'
import * as zxcvbnDePackage from '@zxcvbn-ts/language-de'
import { matcherPwnedFactory } from '@zxcvbn-ts/matcher-pwned'

const myPlugin = {
  install(Vue) {
    // optional
    const matcherPwned = matcherPwnedFactory(fetch)
    const customMatcher = {
      pwned: matcherPwned
    }

    const options = {
      // recommended
      dictionary: {
        ...zxcvbnCommonPackage.dictionary,
        ...zxcvbnEnPackage.dictionary,
        // recommended the language of the country that the user will be in
        ...zxcvbnDePackage.dictionary,
      },
      // recommended
      graphs: zxcvbnCommonPackage.adjacencyGraphs,
      // recommended
      useLevenshteinDistance: true,
      // optional
      translations: zxcvbnEnPackage.translations,
    }

    Vue.prototype.$zxcvbn = new ZxcvbnFactory(options, customMatcher)
  },
}
<template>
  <div class="example">
    <label>
      Password
      <input v-model="password" type="text" />
    </label>
    <template v-if="result">
      <div>The password score is {{ result.score }}/4</div>
    </template>
  </div>
</template>

<script>
import { debounce } from '@zxcvbn-ts/core'

export default {
  name: 'ZxcvbnInput',
  data() {
    return {
      password: '',
      result: null,
      // recommended
      debounce: debounce(this.useZxcvbn, 200),
    }
  },
  methods: {
    async useZxcvbn() {
      if (this.password) {
        this.result = await this.$zxcvbn.checkAsync(this.password)
      } else {
        this.result = null
      }
    },
  },
  watch: {
    password() {
      this.debounce()
    },
  },
}
</script>

nuxt

Use a module to define the options on the server side or/and a client only plugin for the client. Most of the time you don't need to add the module because the user works only on the client and doesn't type passwords for the server renderer.

/plugins/zxcvbn.ts

import { matcherPwnedFactory } from '@zxcvbn-ts/matcher-pwned'
import { ZxcvbnFactory } from '@zxcvbn-ts/core'
import { OptionsType } from '@zxcvbn-ts/core/dist/types'
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en'
import { ModuleOptions, Nuxt } from '@nuxt/schema'

export default defineNuxtPlugin(({app}, inject) => {
  const matcherPwned = matcherPwnedFactory(fetch)
  const customMatcher = {
    pwned: matcherPwned
  }

  const options: OptionsType = {
    // recommended
    dictionary: {
      ...zxcvbnCommonPackage.dictionary,
      ...zxcvbnEnPackage.dictionary,
    },
    // recommended
    graphs: zxcvbnCommonPackage.adjacencyGraphs,
    // recommended
    useLevenshteinDistance: true,
    // optional
    translations: zxcvbnEnPackage.translations,
  }

  inject('zxcvbn', new ZxcvbnFactory(options, customMatcher))
})

nuxt.config.ts

export default defineNuxtConfig({
  // add plugin for client only to load the options on the client side
  plugins: [{ src: '~/plugins/zxcvbn.ts', mode: 'client' }],
  build: {
    // add if needed for your setup
    transpile: ['@zxcvbn-ts/matcher-pwned'],
  },
})

ZxcvbnInput.vue

<template>
  <div class="example">
    <label>
      Password
      <input v-model="password" type="text" />
    </label>
    <template v-if="result">
      <div>The password score is {{ result.score }}/4</div>
    </template>
  </div>
</template>

<script lang="ts" setup>
import { debounce, ZxcvbnResult } from '@zxcvbn-ts/core'
import { Ref, watch } from '@vue/runtime-core'

let password = ref()
let result: Ref<ZxcvbnResult | null> = ref(null)

const useZxcvbn = async () => {
  if (password) {
    result.value = await this.$zxcvbn.checkAsync(password.value)
  } else {
    result.value = null
  }
}

const zxcvbnDebounce = debounce(useZxcvbn, 200, false)

watch(password, zxcvbnDebounce)
</script>

React

import { useState, useEffect, useDeferredValue } from 'react'
import { ZxcvbnFactory, ZxcvbnResult } from '@zxcvbn-ts/core'
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en'
import * as zxcvbnDePackage from '@zxcvbn-ts/language-de'
import { matcherPwnedFactory } from '@zxcvbn-ts/matcher-pwned'

const options = {
  // recommended
  dictionary: {
    ...zxcvbnCommonPackage.dictionary,
    ...zxcvbnEnPackage.dictionary,
    // recommended the language of the country that the user will be in
    ...zxcvbnDePackage.dictionary,
  },
  // recommended
  graphs: zxcvbnCommonPackage.adjacencyGraphs,
  // recommended
  useLevenshteinDistance: true,
  // optional
  translations: zxcvbnEnPackage.translations,
}

// optional
const matcherPwned = matcherPwnedFactory(fetch)
const customMatcher = {
  pwned: matcherPwned
}
const zxcvbn = new ZxcvbnFactory(options, customMatcher)

const usePasswordStrength = (password: string) => {
  const [result, setResult] = useState<ZxcvbnResult | null>(null)
  // NOTE: useDeferredValue is React v18 only, for v17 or lower use debouncing
  const deferredPassword = useDeferredValue(password)

  useEffect(() => {
    zxcvbn.checkAsync(deferredPassword).then((response) => setResult(response))
  }, [deferredPassword])

  return result
}

export default function PasswordStrength() {
  const [password, setPassword] = useState<string>('')
  const result = usePasswordStrength(password)
  return (
    <div>
      <label>
        Password:
        <input
          value={password}
          type="text"
          onChange={(e) => {
            setPassword(e.target.value)
          }}
        />
      </label>
      {result && <div>The password score is {result.score}/4</div>}
    </div>
  )
}

Angular

tbd.

Fastify

import Fastify from 'fastify'
import { ZxcvbnFactory } from '@zxcvbn-ts/core'
import * as zxcvbnCommonPackage from '@zxcvbn-ts/language-common'
import * as zxcvbnEnPackage from '@zxcvbn-ts/language-en'

const fastify = Fastify()

const options = {
  // recommended
  dictionary: {
    ...zxcvbnCommonPackage.dictionary,
    ...zxcvbnEnPackage.dictionary,
  },
  // recommended
  graphs: zxcvbnCommonPackage.adjacencyGraphs,
  // recommended
  useLevenshteinDistance: true,
  // optional
  translations: zxcvbnEnPackage.translations,
}

const zxcvbn = new ZxcvbnFactory(options)

fastify.post<{Body: string}>('/password-strength', async (request, reply) => {
  if (request.body) {
    return zxcvbn.check(request.body)
  }
  return reply.status(400).send({ error: 'Password is required' })
})

const start = async () => {
  try {
    await fastify.listen({ port: 3000 })
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()
Edit this page
Last Updated: 6/11/26, 7:17 AM
Contributors: MrWook, gabberr, Martin Trobäck, Stoney Shou
Prev
Options
Next
Migration