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 { zxcvbnOptions } 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() {
    // optional
    const matcherPwned = matcherPwnedFactory(fetch, zxcvbnOptions)
    zxcvbnOptions.addMatcher('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,
    }
    zxcvbnOptions.setOptions(options)
  },
}
<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 { zxcvbn, debounce } from '@zxcvbn-ts/core'

export default {
  name: 'ZxcvbnInput',
  data() {
    return {
      password: '',
      result: null,
      // recommended
      debounce: debounce(this.useZxcvbn, 200),
    }
  },
  methods: {
    setResult(result) {
      this.result = result
    },
    async useZxcvbn() {
      if (this.password) {
        this.result = await zxcvbn(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.

/modules/zxcvbn.ts

import { matcherPwnedFactory } from '@zxcvbn-ts/matcher-pwned'
import { zxcvbnOptions } 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 function zxcvbnModule(moduleOptions: ModuleOptions, nuxt: Nuxt) {
  nuxt.hook('ready', async () => {
    const matcherPwned = matcherPwnedFactory(fetch, zxcvbnOptions)
    zxcvbnOptions.addMatcher('pwned', matcherPwned)

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

/plugins/zxcvbn.ts

import { matcherPwnedFactory } from '@zxcvbn-ts/matcher-pwned'
import { zxcvbnOptions } 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(() => {
  const matcherPwned = matcherPwnedFactory(fetch, zxcvbnOptions)
  zxcvbnOptions.addMatcher('pwned', matcherPwned)

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

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' }],
  // add module to load the options once for server side
  modules: ['~/modules/zxcvbn.ts'],
  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 { zxcvbnAsync, 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 zxcvbnAsync(password.value)
  } else {
    result.value = null
  }
}

const zxcvbnDebounce = debounce(useZxcvbn, 200, false)

watch(password, zxcvbnDebounce)
</script>

React

import { useState, useEffect, useDeferredValue } from 'react'
import { zxcvbnOptions, zxcvbnAsync, 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'

// optional
const matcherPwned = matcherPwnedFactory(fetch, zxcvbnOptions)
zxcvbnOptions.addMatcher('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,
}
zxcvbnOptions.setOptions(options)

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(() => {
    zxcvbnAsync(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.