Skip to content

usePasswordStrength ​

Compute a coarse-grained password strength based on length and character class diversity. Returns a ComputedRef<Result | null> that emits null while the password is empty.

The result reports the password length, the matched rules (lowercase, uppercase, number, symbol) and a strength bucket (too_weak, weak, medium, strong).

Demo ​

Strength: —

  • lowercase
  • uppercase
  • number
  • symbol

<script setup lang="ts">
    import { computed, ref } from 'vue';
    import { usePasswordStrength } from '@basmilius/common';

    const password = ref('');
    const result = usePasswordStrength(password);

    const allRules = ['lowercase', 'uppercase', 'number', 'symbol'] as const;

    const labels = {
        too_weak: 'Too weak',
        weak: 'Weak',
        medium: 'Medium',
        strong: 'Strong'
    } as const;

    const label = computed(() => result.value ? labels[result.value.strength] : '—');
</script>

<template>
    <div class="demo">
        <input v-model="password" class="input" type="text" placeholder="Type a password…"/>

        <div class="bar" :data-strength="result?.strength ?? 'none'">
            <span/>
            <span/>
            <span/>
            <span/>
        </div>

        <p class="label">Strength: <b>{{ label }}</b></p>

        <ul class="rules">
            <li v-for="rule in allRules" :key="rule" :class="{met: result?.rules.includes(rule)}">
                {{ rule }}
            </li>
        </ul>
    </div>
</template>

<style scoped>
    .demo {
        padding: 24px;
        border: 1px solid var(--vp-c-border);
        border-radius: 12px;
        background: var(--vp-c-bg-soft);
    }

    .input {
        width: 100%;
        margin-bottom: 16px;
        padding: 8px 12px;
        border: 1px solid var(--vp-c-border);
        border-radius: 8px;
        background: var(--vp-c-bg);
        font-size: 14px;
        color: var(--vp-c-text-1);
    }

    .bar {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        gap: 6px;
        margin-bottom: 12px;
    }

    .bar span {
        height: 6px;
        border-radius: 999px;
        background: var(--vp-c-bg);
        box-shadow: 0 0 0 1px var(--vp-c-border) inset;
    }

    .bar[data-strength='too_weak'] span:nth-child(-n + 1),
    .bar[data-strength='weak'] span:nth-child(-n + 2),
    .bar[data-strength='medium'] span:nth-child(-n + 3),
    .bar[data-strength='strong'] span:nth-child(-n + 4) {
        background: var(--vp-c-brand-1);
        box-shadow: none;
    }

    .label {
        margin: 0 0 12px;
        font-size: 14px;
        color: var(--vp-c-text-2);
    }

    .rules {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        margin: 0;
        padding: 0;
        list-style: none;
    }

    .rules li {
        padding: 4px 12px;
        border-radius: 999px;
        background: var(--vp-c-bg);
        box-shadow: 0 0 0 1px var(--vp-c-border) inset;
        font-family: var(--vp-font-family-mono);
        font-size: 12px;
        color: var(--vp-c-text-3);
    }

    .rules li.met {
        color: var(--vp-c-brand-1);
        box-shadow: 0 0 0 1px var(--vp-c-brand-1) inset;
    }
</style>

Importing ​

ts
import { usePasswordStrength } from '@basmilius/common';

Usage ​

vue
<script setup lang="ts">
    import { ref } from 'vue';
    import { usePasswordStrength } from '@basmilius/common';

    const password = ref('');
    const strength = usePasswordStrength(password);
</script>

<template>
    <input v-model="password" type="password"/>
    <p v-if="strength">
        Strength: {{ strength.strength }} ({{ strength.length }} chars,
        {{ strength.rules.length }} rule(s) matched)
    </p>
</template>

Strength buckets ​

BucketMinimum lengthMinimum diversity
too_weak00
weak62
medium83
strong104

Diversity counts how many of the four rules (lowercase, uppercase, number, symbol) the password matches. The resulting bucket is the highest one whose length and diversity thresholds are both satisfied.

Result shape ​

ts
type PasswordRuleType =
    | 'lowercase'
    | 'uppercase'
    | 'number'
    | 'symbol';

type PasswordStrengthType =
    | 'too_weak'
    | 'weak'
    | 'medium'
    | 'strong';

type Result = {
    readonly length: number;
    readonly rules: PasswordRuleType[];
    readonly strength: PasswordStrengthType;
};

Type signature ​

ts
declare function usePasswordStrength(
    passwordRef: Ref<string>
): ComputedRef<Result | null>;