PasswordInput

Capture password data from user

PackageIcon

Usage

PasswordInput component supports Input and Input.Wrapper component features and all input element props. The PasswordInput documentation does not include all features supported by the component – see the Input documentation to learn about all available features.

Input description

Variant
Size
Radius
import { PasswordInput } from '@mantine/core';

function Demo() {
  return (
    <PasswordInput
      label="Input label"
      description="Input description"
      placeholder="Input placeholder"
    />
  );
}

Loading state

Set loading prop to display a loading indicator. By default, the loader is displayed on the right side of the input. You can change the position with the loadingPosition prop to 'left' or 'right'. This is useful for async operations like API calls, searches, or validations:

import { PasswordInput } from '@mantine/core';

function Demo() {
  return <PasswordInput placeholder="Your password" loading />;
}

Controlled

import { useState } from 'react';
import { PasswordInput } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState('');
  return (
    <PasswordInput
      value={value}
      onChange={(event) => setValue(event.currentTarget.value)}
    />
  );
}

Uncontrolled

PasswordInput can be used with uncontrolled forms the same way as a native input[type="password"]. Set the name attribute to include password input value in FormData object on form submission. To control the initial value in uncontrolled forms, use the defaultValue prop.

Example usage of uncontrolled PasswordInput with FormData:

import { PasswordInput } from '@mantine/core';

function Demo() {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log('Password value:', formData.get('password'));
      }}
    >
      <PasswordInput
        label="Enter your password"
        name="password"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Controlled visibility toggle

Control the visibility state with the visible and onVisibilityChange props. For example, the props can be used to sync visibility state between two inputs:

import { useDisclosure } from '@mantine/hooks';
import { PasswordInput, Stack } from '@mantine/core';

function Demo() {
  const [visible, { toggle }] = useDisclosure(false);
  return (
    <Stack>
      <PasswordInput
        label="Password"
        defaultValue="secret"
        visible={visible}
        onVisibilityChange={toggle}
      />
      <PasswordInput
        label="Confirm password"
        defaultValue="secret"
        visible={visible}
        onVisibilityChange={toggle}
      />
    </Stack>
  );
}

Change visibility toggle icon

To change the visibility toggle icon, pass a React component that accepts the reveal prop to visibilityToggleIcon:

import { PasswordInput } from '@mantine/core';
import { EyeIcon, EyeSlashIcon } from '@phosphor-icons/react';

const VisibilityToggleIcon = ({ reveal }: { reveal: boolean }) =>
  reveal ? (
    <EyeSlashIcon style={{ width: 'var(--psi-icon-size)', height: 'var(--psi-icon-size)' }} />
  ) : (
    <EyeIcon style={{ width: 'var(--psi-icon-size)', height: 'var(--psi-icon-size)' }} />
  );

function Demo() {
  return (
    <PasswordInput
      maw={320}
      mx="auto"
      label="Change visibility toggle icon"
      placeholder="Change visibility toggle icon"
      defaultValue="secret"
      visibilityToggleIcon={VisibilityToggleIcon}
    />
  );
}

Strength meter example

Password strength meter example with the Progress and Popover components:

import { useState } from 'react';
import { XIcon, CheckIcon } from '@phosphor-icons/react';
import { PasswordInput, Progress, Text, Popover, Box } from '@mantine/core';

function PasswordRequirement({ meets, label }: { meets: boolean; label: string }) {
  return (
    <Text
      c={meets ? 'teal' : 'red'}
      style={{ display: 'flex', alignItems: 'center' }}
      mt={7}
      size="sm"
    >
      {meets ? <CheckIcon size={14} /> : <XIcon size={14} />}
      <Box ml={10}>{label}</Box>
    </Text>
  );
}

const requirements = [
  { re: /[0-9]/, label: 'Includes number' },
  { re: /[a-z]/, label: 'Includes lowercase letter' },
  { re: /[A-Z]/, label: 'Includes uppercase letter' },
  { re: /[$&+,:;=?@#|'<>.^*()%!-]/, label: 'Includes special symbol' },
];

function getStrength(password: string) {
  let multiplier = password.length > 5 ? 0 : 1;

  requirements.forEach((requirement) => {
    if (!requirement.re.test(password)) {
      multiplier += 1;
    }
  });

  return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 10);
}

function Demo() {
  const [popoverOpened, setPopoverOpened] = useState(false);
  const [value, setValue] = useState('');
  const checks = requirements.map((requirement, index) => (
    <PasswordRequirement key={index} label={requirement.label} meets={requirement.re.test(value)} />
  ));

  const strength = getStrength(value);
  const color = strength === 100 ? 'teal' : strength > 50 ? 'yellow' : 'red';

  return (
    <Popover opened={popoverOpened} position="bottom" width="target" transitionProps={{ transition: 'pop' }}>
      <Popover.Target>
        <div
          onFocusCapture={() => setPopoverOpened(true)}
          onBlurCapture={() => setPopoverOpened(false)}
        >
          <PasswordInput
            withAsterisk
            label="Your password"
            placeholder="Your password"
            value={value}
            onChange={(event) => setValue(event.currentTarget.value)}
          />
        </div>
      </Popover.Target>
      <Popover.Dropdown>
        <Progress color={color} value={strength} size={5} mb="xs" />
        <PasswordRequirement label="Includes at least 6 characters" meets={value.length > 5} />
        {checks}
      </Popover.Dropdown>
    </Popover>
  );
}

Usage without visibility toggle

If you do not need the visibility toggle button, use the TextInput component instead:

import { TextInput } from '@mantine/core';

function Demo() {
  return <TextInput type="password" />;
}

Left and right sections

PasswordInput supports leftSection and rightSection props. These sections are rendered with absolute positioning inside the input wrapper. You can use them to display icons, input controls, or any other elements.

You can use the following props to control sections styles and content:

  • rightSection / leftSection – React node to render on the corresponding side of input
  • rightSectionWidth/leftSectionWidth – controls the width of the right section and padding on the corresponding side of the input. By default, it is controlled by the component size prop.
  • rightSectionPointerEvents/leftSectionPointerEvents – controls the pointer-events property of the section. If you want to render a non-interactive element, set it to none to pass clicks through to the input.

Note that when the rightSection prop is used, the visibility toggle button is not rendered.

import { PasswordInput } from '@mantine/core';
import { LockIcon } from '@phosphor-icons/react';

function Demo() {
  const icon = <LockIcon size={18} />;

  return (
    <>
      <PasswordInput
        leftSection={icon}
        leftSectionPointerEvents="none"
        label="With left section"
        placeholder="With left section"
      />
      <PasswordInput
        rightSection={icon}
        label="With right section"
        placeholder="With right section"
        rightSectionPointerEvents="none"
        mt="md"
      />
    </>
  );
}

Error state

Invalid name

import { PasswordInput } from '@mantine/core';

function Demo() {
  return (
    <>
      <PasswordInput label="Boolean error" placeholder="Boolean error" error />
      <PasswordInput
        mt="md"
        label="With error message"
        placeholder="With error message"
        error="Invalid name"
      />
    </>
  );
}

Disabled

When the disabled prop is set, the visibility toggle button is hidden:

import { PasswordInput } from '@mantine/core';

function Demo() {
  return (
    <PasswordInput disabled label="Disabled password input" placeholder="Disabled password input" />
  );
}

Styles API

PasswordInput supports the Styles API; you can add styles to any inner element of the component with the classNames prop. Follow the Styles API documentation to learn more.

Description

Error

Component Styles API

Hover over selectors to highlight corresponding elements

/*
 * Hover over selectors to apply outline styles
 *
 */

Get element ref

import { useRef } from 'react';
import { PasswordInput } from '@mantine/core';

function Demo() {
  const ref = useRef<HTMLInputElement>(null);
  return <PasswordInput ref={ref} />;
}

Accessibility

If PasswordInput is used without the label prop, it will not be announced properly by screen readers:

import { PasswordInput } from '@mantine/core';

// Inaccessible input – screen reader will not announce it properly
function Demo() {
  return <PasswordInput />;
}

Set aria-label to make the input accessible. In this case the label will not be visible, but screen readers will announce it:

import { PasswordInput } from '@mantine/core';

// Accessible input – it has aria-label
function Demo() {
  return <PasswordInput aria-label="My input" />;
}

If the label prop is set, the input will be accessible and it is not required to set aria-label:

import { PasswordInput } from '@mantine/core';

// Accessible input – it has associated label element
function Demo() {
  return <PasswordInput label="My input" />;
}

To set aria-label on the visibility toggle button, use the visibilityToggleButtonProps prop:

import { PasswordInput } from '@mantine/core';

function Demo() {
  return (
    <PasswordInput
      label="Password"
      visibilityToggleButtonProps={{
        'aria-label': 'Toggle password visibility',
      }}
    />
  );
}