ReactForm
Home/Tools/Password Field

React Form with Password Field

A password field with show/hide toggle and a strength indicator covers the two most common UX requests for authentication forms. The strength indicator is purely visual feedback — not actual security enforcement — but it motivates users to choose stronger passwords by making the assessment immediate and clear.

ReactForm Team·May 2026·4 min read

The challenge

Password inputs need visibility toggles, strength feedback, and secure handling that the native input type alone does not provide.

  • Toggling between type="password" and type="text" without React losing focus on the input
  • Building a strength heuristic that is meaningful without being so strict it frustrates users
  • Making the show/hide button accessible — it needs a proper aria-label, not just an eye icon
  • Ensuring autocomplete="current-password" or autocomplete="new-password" is set correctly for password managers

Working code example

Here is a complete, self-contained working example you can drop directly into any React project. It uses Tailwind CSS for styling and requires no external dependencies beyond React itself.

PasswordField.jsx
import { useState } from 'react';

function getStrength(pwd) {
  if (pwd.length === 0) return { label: '', color: '', width: '0%' };
  const hasLower = /[a-z]/.test(pwd);
  const hasUpper = /[A-Z]/.test(pwd);
  const hasDigit = /\d/.test(pwd);
  const hasSpecial = /[^a-zA-Z0-9]/.test(pwd);
  const score = [hasLower, hasUpper, hasDigit, hasSpecial, pwd.length >= 10].filter(Boolean).length;
  if (score <= 2) return { label: 'Weak', color: 'bg-red-500', width: '33%' };
  if (score <= 3) return { label: 'Medium', color: 'bg-yellow-500', width: '66%' };
  return { label: 'Strong', color: 'bg-green-500', width: '100%' };
}

export default function PasswordField() {
  const [password, setPassword] = useState('');
  const [show, setShow] = useState(false);
  const [submitted, setSubmitted] = useState(false);

  const strength = getStrength(password);

  const handleSubmit = (e) => {
    e.preventDefault();
    if (password.length < 8) { alert('Password must be at least 8 characters'); return; }
    setSubmitted(true);
  };

  if (submitted)
    return <p className="p-6 text-green-600 font-medium">Password set successfully!</p>;

  return (
    <form onSubmit={handleSubmit} className="max-w-sm mx-auto p-6 bg-white rounded-2xl shadow space-y-4">
      <h2 className="text-xl font-bold text-gray-800">Set Password</h2>
      <div>
        <label className="block text-sm font-medium text-gray-700 mb-1">Password</label>
        <div className="relative">
          <input
            type={show ? 'text' : 'password'}
            value={password}
            onChange={(e) => setPassword(e.target.value)}
            autoComplete="new-password"
            placeholder="Enter a strong password"
            className="w-full border border-gray-300 rounded-lg px-3 py-2 pr-16 text-sm outline-none focus:ring-2 focus:ring-teal-500"
          />
          <button
            type="button"
            onClick={() => setShow((s) => !s)}
            aria-label={show ? 'Hide password' : 'Show password'}
            className="absolute right-3 top-1/2 -translate-y-1/2 text-xs text-teal-600 font-medium hover:underline">
            {show ? 'Hide' : 'Show'}
          </button>
        </div>
      </div>

      {password.length > 0 && (
        <div>
          <div className="h-1.5 w-full bg-gray-200 rounded-full overflow-hidden">
            <div
              className={`h-full rounded-full transition-all duration-300 ${strength.color}`}
              style={{ width: strength.width }}
            />
          </div>
          <p className={`text-xs mt-1 ${
            strength.label === 'Strong' ? 'text-green-600' :
            strength.label === 'Medium' ? 'text-yellow-600' : 'text-red-500'
          }`}>
            {strength.label} password
          </p>
        </div>
      )}

      <p className="text-xs text-gray-400">
        Use 10+ characters with uppercase, lowercase, numbers, and symbols for a strong password.
      </p>

      <button type="submit"
        className="w-full bg-teal-600 text-white font-semibold py-2 rounded-lg hover:bg-teal-700 transition">
        Set Password
      </button>
    </form>
  );
}

How ReactForm.co helps

ReactForm's visual builder handles all of the above — password field configuration, validation rules, state management, and responsive layout — without writing a single line of code. Drag fields onto the canvas, configure their properties in the sidebar, and get production-ready React output. You can publish the form and collect responses instantly, or export the JSX to drop into your own codebase.

Build this form visually

Frequently asked questions

How do I add a show/hide password toggle in React?

Store a boolean showPassword in state. Conditionally set the input's type attribute: type={showPassword ? "text" : "password"}. Add a button with onClick={() => setShowPassword(s => !s)}. Use a button element (not a div or span) so it is keyboard accessible. Give it a descriptive aria-label for screen readers.

How do I build a password strength indicator?

Write a function that scores the password based on criteria like length, uppercase, lowercase, digits, and special characters. Map the score to a label (Weak / Medium / Strong) and a color. Render a progress bar whose width reflects the score. Run this function on every keystroke — it is fast enough that debouncing is unnecessary.

What autocomplete value should I use on a password field?

Use autocomplete="new-password" on registration or change-password forms so password managers offer to generate and save a new password. Use autocomplete="current-password" on login forms so password managers autofill the saved password. Using autocomplete="off" actively harms usability and is ignored by most modern browsers.

Related topics

Build this form visually — no code needed

ReactForm.co handles password field fields, validation, conditional logic, and responsive layout automatically. Publish in minutes and collect responses for free.