ARTICLE AD BOX
The underlying issue is that some form attributes send conflicting signals to the browser, which ends up confusing the built in password manager.
Things to remove or simplify:
action="/login"
Browsers see this and assume a traditional full page form submission.
method="POST" This reinforces the expectation of a normal navigation based submit.
A hidden username field
If it duplicates the visible email input, the browser can no longer reliably tell which field represents the username.
autoComplete="username email" For an email field, use only autoComplete="email". Combining values can break credential detection.
A working form looks like this:
form onSubmit={handleSubmit} input type="email" name="email" autoComplete="email" value={email} onChange={(e) => setEmail(e.target.value)} input type="password" name="password" autoComplete="current-password" value={password} onChange={(e) => setPassword(e.target.value)} button type="submit" Sign in /formWhy this works:
Modern browsers look for an input of type password with autoComplete="current-password", paired with a related email or username field using autoComplete="email" or autoComplete="username". They also watch for a form submission event, even when preventDefault() is used in a single page application.
When legacy attributes like action and method are present, the browser expects the form to navigate to a new page. Since SPAs intentionally block that navigation, the browser assumes the login did not complete successfully and does not offer to save credentials.
For registration or password creation flows, use autoComplete="new-password" instead of current-password so the browser can distinguish between signup and login.
