Password Strength
Chassis PasswordStrength renders a segmented meter or progress bar giving users real-time feedback on password complexity as they type.
Overview
PasswordStrength scores a password against a set of weighted criteria (length, character variety, special characters) and renders the result as either four discrete segments or a single growing bar, both styled by chassis-css's .strength/.strength-bar. It's display-only — pass it the current password value from a controlled <input>, and it computes and renders the rest.
import { useState } from 'react'
import { TextInput, FormField, PasswordStrength } from '@chassis-ui/react'
export const Example = () => {
const [password, setPassword] = useState('')
return (
<FormField label="Password" ids={{ input: 'password1', label: 'password1Label' }}>
<TextInput
aria-labelledby="password1Label"
autoComplete="new-password"
id="password1"
onChange={setPassword}
placeholder="Enter password"
type="password"
value={password}
/>
<PasswordStrength value={password} />
</FormField>
)
}Progress bar variant
Pass variant="bar" for a single bar that grows with password strength instead of four segments.
import { useState } from 'react'
import { TextInput, FormField, PasswordStrength } from '@chassis-ui/react'
export const Example = () => {
const [password, setPassword] = useState('')
return (
<FormField label="Password" ids={{ input: 'password2', label: 'password2Label' }}>
<TextInput
aria-labelledby="password2Label"
autoComplete="new-password"
id="password2"
onChange={setPassword}
placeholder="Enter password"
type="password"
value={password}
/>
<PasswordStrength value={password} variant="bar" />
</FormField>
)
}As a form field
Associate the meter's text feedback with the password field via aria-describedby, alongside a FormHelp describing the requirements.
import { useState } from 'react'
import { TextInput, FormField, PasswordStrength } from '@chassis-ui/react'
export const Example = () => {
const [password, setPassword] = useState('')
return (
<FormField
label="Password"
help="Use 8 or more characters with a mix of letters, numbers & symbols."
ids={{ help: 'password3Help', input: 'password3', label: 'password3Label' }}
>
<TextInput
aria-describedby="password3Help"
aria-labelledby="password3Label"
autoComplete="new-password"
id="password3"
onChange={setPassword}
placeholder="Enter password"
type="password"
value={password}
/>
<PasswordStrength value={password} />
</FormField>
)
}Strength levels
Four levels — weak, fair, good, and strong — are driven by the default scoring criteria below.
import { PasswordStrength } from '@chassis-ui/react'
const samples: { label: string; password: string }[] = [
{ label: 'Weak', password: 'abc' },
{ label: 'Fair', password: 'abcdefgh1' },
{ label: 'Good', password: 'Abcdefgh1234' },
{ label: 'Strong', password: 'Sup3r!Secret!Passphrase99' }
]
export const Example = () => {
return (
<div className="vstack gap-md">
{samples.map(({ label, password }) => (
<div key={label}>
<div className="fg-subtle">{label}</div>
<PasswordStrength aria-label={`${label} example`} value={password} />
</div>
))}
</div>
)
}Custom weights and thresholds
Pass weights to change or disable individual scoring criteria (set a criterion to 0 to drop it entirely), thresholds to move the [weak, fair, good] score boundaries, and messages to customize the feedback text.
import { useState } from 'react'
import { TextInput, FormField, PasswordStrength } from '@chassis-ui/react'
export const Example = () => {
const [password, setPassword] = useState('')
return (
<FormField label="Password" ids={{ input: 'password4', label: 'password4Label' }}>
<TextInput
aria-labelledby="password4Label"
autoComplete="new-password"
id="password4"
onChange={setPassword}
placeholder="No uppercase required, symbols count double"
type="password"
value={password}
/>
<PasswordStrength
messages={{ weak: 'Too weak', strong: 'Excellent!' }}
thresholds={[3, 5, 7]}
value={password}
weights={{ special: 2, uppercase: 0 }}
/>
</FormField>
)
}Strength criteria
The default scoring algorithm evaluates passwords based on:
| Criteria | Points |
|---|---|
Meets minimum length (minLength, 8 by default) | +1 |
4+ characters over minLength | +1 |
| Contains lowercase letters | +1 |
| Contains uppercase letters | +1 |
| Contains numbers | +1 |
| Contains special characters | +1 |
| Multiple special characters | +1 |
| 16+ characters | +1 |
| Level | Points (default thresholds) |
|---|---|
| Weak | 1–2 |
| Fair | 3–4 |
| Good | 5–6 |
| Strong | 7–8 |
Pass scorer to replace this algorithm entirely with a custom (password) => number function.
Accessibility
The meter carries role="meter" (via React Aria's useMeter) with aria-valuenow/aria-valuemin/aria-valuemax kept in sync with the score, and an accessible name from aria-label (defaults to "Password strength"). The .strength-text feedback element is an aria-live="polite" region, so screen reader users hear the level change as they type — the meter is decorative feedback and should always be paired with visible password requirements text, not relied on alone.
Scope
Built on React Aria's useMeter for the accessible display — there's no dedicated password-strength hook, so the scoring algorithm itself is ported directly from chassis-css's vanilla strength.js (weights/thresholds/scorer options included) rather than coming from the library.
Unlike the vanilla component, PasswordStrength doesn't look up a sibling password <input> itself or expose imperative getStrength()/evaluate() methods or a strengthChange DOM event — pass the current password as value and read the result declaratively via onStrengthChange instead.
API
PasswordStrength
| Prop | Type | Default |
|---|---|---|
aria-label? | string | Password strength |
An accessible label for the meter. Defaults to "Password strength". | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
id? | string | — |
id forwarded to the meter element. | ||
maxScore? | number | — |
The maximum possible score, used to compute the meter's aria-valuemax. Defaults to the sum
of weights' values — set this explicitly when pairing a custom scorer whose scale doesn't
match the built-in criteria weights, otherwise aria-valuenow/aria-valuemax get silently
clamped to the built-in max. | ||
messages? | Partial<Record<StrengthLevel, string>> | — |
Feedback message shown for each strength level, when showText is true. | ||
minLength? | number | 8 |
Minimum password length required to earn the first strength point. Defaults to 8. | ||
onStrengthChange? | ((result: { score: number; strength: StrengthLevel | null; }) => void) | — |
Callback fired whenever the strength level changes, including once on mount with the
initial value's strength. | ||
scorer? | ((password: string) => number) | — |
| Custom scoring function overriding the built-in criteria. Receives the password and returns a numeric score. | ||
showText? | boolean | true |
Renders a .strength-text element below the meter with the current level's message.
Defaults to true. | ||
thresholds? | [number, number, number] | [2, 4, 6] |
Score boundaries [weak, fair, good] — scores above the last value are "strong". Defaults
to [2, 4, 6]. | ||
value | string | — |
| The current password value to evaluate. | ||
variant? | "bar" | "segmented" | segmented |
Render as four discrete segments, or a single growing bar. Defaults to "segmented". | ||
weights? | Partial<StrengthWeights> | — |
Point values for each scoring criterion. Set a criterion to 0 to disable it. Merged with
the built-in defaults. | ||