Password Input
A native password field with a built-in show / hide toggle, in the shared input shell — bordered box, focus ring, and invalid / disabled states, plus a built-in label, description, and error.
PasswordInput is a native password field with a built-in reveal toggle. It renders an <input type="password"> inside the same shell as Input and adds an inline-end button that flips the field between masked and visible. Like the rest of the family it carries its own label, description, error, and required — no external wrapper needed — and passing error flips it to the invalid (red) state. Omit them and it still composes cleanly inside a Field.
import { PasswordInput } from '@42-central/ui-react/password-input';
<PasswordInput name="password" placeholder="Enter a password" />It is an event-based control, like Input: value, onChange, name, and ref forward to the underlying <input>, so React Hook Form's register and a plain onChange(event) handler both work as usual. The show / hide state is owned internally by default and never leaks into the form value; lift it out with visible when something else needs to drive it.
Playground
Component
Dir
Presets
Props
Code
<Component
label="Password"
size="md"
placeholder="Enter a password"
/>Variants
Two shells. default is bordered; unstyled strips the chrome but keeps the layout (height + the reveal toggle).
Code
<PasswordInput variant="default" placeholder="Default" />
<PasswordInput variant="unstyled" placeholder="Unstyled" />Sizes
Five steps — xs through xl. The control font, padding, and the toggle icon scale to match Input.
Code
<PasswordInput size="xs" placeholder="xs" />
<PasswordInput size="sm" placeholder="sm" />
<PasswordInput size="md" placeholder="md" />
<PasswordInput size="lg" placeholder="lg" />
<PasswordInput size="xl" placeholder="xl" />States
invalid swaps the border and focus ring to red regardless of color (and sets aria-invalid). disabled dims the shell and disables both the input and the toggle; readOnly keeps full contrast and stays focusable but rejects edits.
Code
<PasswordInput label="Invalid" invalid defaultValue="weak" />
<PasswordInput label="Disabled" disabled defaultValue="secret" />
<PasswordInput label="Read-only" readOnly defaultValue="secret" />With label, description & error
label, description (helper text), and error are built in via Field — no external wrapper needed. Passing error flips the shell to the invalid (red) state; required adds the semantic flag and a red asterisk on the label.
Code
<PasswordInput label="Password" description="At least 8 characters." placeholder="Enter a password" />
<PasswordInput label="Password" required placeholder="Enter a password" />
<PasswordInput label="Password" error="Password is too weak" placeholder="Enter a password" />If you omit these props, PasswordInput still composes cleanly inside a Field: disabled / invalid / readOnly / required propagate from the Field, and the aria-* relationships are wired for you.
import { Field } from '@42-central/ui-react/field';
import { PasswordInput } from '@42-central/ui-react/password-input';
<Field label="Password" description="At least 8 characters." required>
<PasswordInput name="password" placeholder="Enter a password" />
</Field>Controlled visibility
visible / defaultVisible / onVisibleChange lift the reveal state out of the field. A common case is one "Show passwords" checkbox for a password and its confirmation: drive both from shared state, and pass hideToggle so the per-field buttons don't fight the checkbox.
Code
const [visible, setVisible] = useState(false)
<PasswordInput label="Password" visible={visible} hideToggle />
<PasswordInput label="Confirm password" visible={visible} hideToggle />
<Checkbox
label="Show passwords"
checked={visible}
onCheckedChange={(checked) => setVisible(checked === true)}
/>Keep the built-in toggle and pass onVisibleChange instead when you only need to observe or veto a press. It receives the next state as a plain boolean, and a controlled field changes only once you feed that back into visible.
Custom toggle
renderToggle is called with the current state and renders inside the kit's button, so the aria-controls / aria-label wiring and the keyboard handling stay intact while you swap the icon. hideToggle renders no button at all.
Code
<PasswordInput
label="Password"
defaultValue="secret"
renderToggle={(visible) => (visible ? <LockKeyholeOpen /> : <LockKeyhole />)}
/>The button's accessible name still comes from translations, never from what renderToggle renders. An icon has no name of its own, so that is what you want. Visible text needs translations too: without it the button keeps the English "Show password", which overrides your "Afficher". That fails WCAG 2.5.3 Label in Name, because a voice-control user who says "click Afficher" hits nothing. Return a label that contains the visible text. In development the kit warns in the console when it doesn't.
Code
<PasswordInput
label="Mot de passe"
defaultValue="secret"
renderToggle={(visible) => (visible ? 'Masquer' : 'Afficher')}
translations={{
visibilityTrigger: (visible) =>
visible ? 'Masquer le mot de passe' : 'Afficher le mot de passe',
}}
/>Translations
The button's accessible label comes from translations.visibilityTrigger, called with the current state. It is the only text the component renders on its own. Keep it state-dependent, since the label is what tells AT whether the value is shown, and make it contain any text a custom renderToggle shows.
Code
<PasswordInput
label="Mot de passe"
translations={{
visibilityTrigger: (visible) =>
visible ? 'Masquer le mot de passe' : 'Afficher le mot de passe',
}}
/>API
Prop
Type
name, placeholder, id, ref, and onBlur forward to the underlying <input>. className styles the shell root. testId lands on that same <input> — see the Testing guide.
Accessibility
- Pass
label/description/errorto wire the accessible name andaria-*relationships via the built-inField. Omit them and it composes inside an outerField(or your own<label htmlFor>) instead. - The reveal toggle is a real
<button>witharia-controlspointing at the input. It is exposed to AT rather than nested in anaria-hiddendecoration. - Its state lives in its label, which flips between "Show password" and "Hide password" (see Translations). It carries no
aria-pressedoraria-expanded: revealing a value doesn't expand anything, and a pressed state on top of a label that already changes would read "Hide password, pressed". If you localize it, keep the label state-dependent. - The toggle is a tab stop right after the input, and Enter / Space toggle it like a click, keeping focus on the button. Pass
toggleTabIndex={-1}to take it out of the tab order, for a form where keyboard users should go straight from field to field; it stays clickable. - A custom
renderTogglerenders inside that same button, so it keeps those attributes, including thearia-label, which overrides the content. If the content is visible text, passtranslationswhose label contains it (WCAG 2.5.3). WithhideToggle, the control you replace it with owns the accessible name. A labelled checkbox works out of the box. invalidsetsaria-invalidon the input.
Changelog
- #312
Control the reveal state with
visible,defaultVisibleandonVisibleChange(visible), for example one "Show passwords" checkbox driving two fields - #322
renderToggle={(visible) => …}replaces the eye icon inside the kit's button, andhideToggleremoves the button when another control owns the state - #312
translations.visibilityTrigger(visible)localizes the reveal button's accessible name - #323
The reveal toggle is in the Tab order and toggles on Enter, Space and screen-reader activation;
toggleTabIndex={-1}restores the old pointer-only button - #324
In development, a warning flags a
renderTogglewhose visible text is missing from the button's accessible name, and points totranslations.visibilityTrigger
- #96
Inputs drop the
filledvariant and thecolorprop
- #77
Field's label names the real control through
aria-labelledby
- #57
Wire the field's description and error to the control with
aria-describedbyandaria-errormessage
Textarea,PasswordInput,NumberInputandPinInput
Textarea
The multi-line sibling of Input — a native textarea in the shared input shell, with bordered box, focus ring, and invalid / disabled states, plus a built-in label, description, and error.
Number Input
A numeric input with increment / decrement steppers on Ark UI's NumberInput machine — clamping, locale-aware formatting, mouse-wheel, and the full keyboard model on the shared input shell.