---
title: Password Input
description: Password input with visibility toggle and strength meter
visibility: guest
draft: false
---

# Password Input

The `<x-wirekit::password-input>` component extends the standard text input with a visibility toggle (eye icon) and an optional password strength meter. The toggle switches between `type="password"` and `type="text"` via Alpine.js without re-rendering.

## Basic Usage

:::preview{title="Password with Toggle"}
<x-wirekit::password-input label="Password" name="password" />
:::

## Without Toggle

:::preview{title="Toggle Disabled"}
<x-wirekit::password-input label="Secret key" name="secret" :toggle="false" />
:::

When `toggle` is `false`, the eye icon button is not rendered and the input behaves like a standard password field.

## With Strength Meter

:::preview{title="Strength Meter Enabled"}
<x-wirekit::password-input label="New Password" name="new_password" :strength-meter="true" hint="Use at least 8 characters with a mix of letters, numbers, and symbols." />
:::

The strength meter evaluates the input in real-time using Alpine.js and displays a colored bar below the field:

- **Weak** — short or common patterns
- **Fair** — meets minimum length but lacks variety
- **Good** — reasonable mix of character types
- **Strong** — long with diverse character types

## Size Variants

:::preview{title="Three Sizes"}
<x-wirekit::password-input name="p-sm" label="Small" size="sm" />

<x-wirekit::password-input name="p-md" label="Medium" size="md" />

<x-wirekit::password-input name="p-lg" label="Large" size="lg" />
:::

## Error State

:::preview{title="Password input with an error message"}
<x-wirekit::password-input label="Password" name="password-error-demo" error="Password must be at least 8 characters." />
:::

## Width

Like all WireKit form components, the password input fills its container (`w-full`). Control the width via the parent element:

```blade
<div class="max-w-sm">
    <x-wirekit::password-input label="Password" name="password" />
</div>
```

See [Input — Width](/components/input#width) for more layout examples (grid columns, mixed widths).

## Optimistic UI

Pass the name of the Livewire method the field should call and the value is sent when you leave the field, shown as saving while it goes:

```blade
<x-wirekit::password-input
    name="secret"
    label="New password"
    optimistic="savePassword"
/>
```

Load `wirekit-optimistic.js` alongside whichever bundle you already use — below it, in your layout:

```blade
@wirekitScripts
<script src="{{ asset('vendor/wirekit/wirekit-optimistic.js') }}"></script>
```

### Try it

The demo below runs the real path: the change shows immediately, the outline says it is
provisional, and the server's answer either confirms it silently or takes it back.

:::preview{title="Optimistic password field — accepted, refused, and a slow answer"}
<livewire:demos.optimistic-host>
<x-wirekit::stack gap="lg" style="max-width: 26rem;">
    <x-wirekit::password-input name="opt-accept" label="Accepted — the confirmation is silent" optimistic="demoAccept" />
    <x-wirekit::password-input name="opt-reject" label="Refused — what you typed stays, and you are told it did not save" optimistic="demoReject" />
    <x-wirekit::password-input name="opt-slow" label="Slow answer — the dashed outline is the provisional state" optimistic="demoSlow" />
</x-wirekit::stack>
</livewire:demos.optimistic-host>
:::

:::source{language="blade"}
{{-- In your app there is no host: your own Livewire component owns the method. --}}
<x-wirekit::password-input
    name="secret"
    label="New password"
    optimistic="savePassword"
/>
:::

The `<livewire:demos.…>` wrapper above exists only on this site — it supplies the demo
methods so the page can show a real round trip. The block under it is what you write.

**A refusal does not take your value back.** For a toggle or a select, putting the old value back costs you nothing — it is simply the other choice. Here it would delete what you typed because a save failed, and re-typing a password is the most expensive re-entry any field can ask for. The value stays, and you are told two things: that it did not save, and that it is still there.

**Nothing reads your password back.** The announcements name no value, and none is written into the page's markup either — the field is driven by the property it already owns rather than by a value handed to the script.

The field is **not** marked invalid. `aria-invalid` means *this value is wrong*, and a save that failed on the network says nothing about the value.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string\|null` | `null` | Label text above the input |
| `hint` | `string\|null` | `null` | Help text below the input |
| `error` | `string\|null` | `null` | Error message (also reads from `$errors`) |
| `name` | `string\|null` | `null` | Form field name |
| `id` | `string\|null` | auto-generated | Element id |
| `size` | `string` | `'md'` | `'sm'`, `'md'`, `'lg'` |
| `toggle` | `bool` | `true` | Show the show/hide password toggle button |
| `strengthMeter` | `bool` | `false` | Show password strength meter below input |
| `optimistic` | `string\|null` | `null` | Livewire method to call when you leave the field, showing the change before the server answers |
| `optimisticArgs` | `array` | `[]` | Extra arguments appended to the optimistic action call, after the new value — the row this control belongs to. |
| `disabled` | `bool` | `false` | Disabled state |
| `scope` | `string\|null` | `null` | Scoped personalization key |

## Accessibility

- Uses native `<input type="password">` — inherits browser autofill and AT support
- The toggle button has `aria-label="Show password"` / `aria-label="Hide password"` (toggles dynamically)
- The toggle button uses `aria-pressed` to communicate current visibility state
- Toggle icons (eye / eye-off) are `aria-hidden="true"`
- Strength meter bar uses `role="meter"` with `aria-valuenow`, `aria-valuemin="0"`, `aria-valuemax="4"`, and `aria-label="Password strength"`
- Strength level text is announced via `aria-live="polite"` for screen readers
- `aria-invalid="true"` and `aria-describedby` set on error or hint

## Keyboard Interaction

| Key | Action |
|-----|--------|
| `Tab` | Move focus to the input |
| Any character | Insert the character (rendered as a masked dot when masked) |
| `Tab` (twice) | Move focus to the show/hide toggle button |
| `Enter` / `Space` (on toggle) | Toggle masked / plain rendering |

## Pitfalls

- **Don't disable the show-password toggle for "security."** Hiding the toggle reduces password quality more than it adds security (cognitive load drives users to weak choices).
- **Don't omit `autocomplete="current-password"` / `"new-password"`.** Browsers and password managers use these to populate correctly; missing values block manager auto-fill.

## Design Tokens

| Token | Purpose |
| --- | --- |
| `--color-wk-bg-input` | Input background |
| `--color-wk-border-strong` | Default border color |
| `--color-wk-border-error` | Border color on error |
| `--color-wk-text` | Input text color |
| `--color-wk-text-muted` | Toggle button icon color |
| `--color-wk-accent` | Toggle button hover color |
| `--color-wk-ring` | Focus ring color |
| `--color-wk-danger` | Strength meter — weak |
| `--color-wk-warning` | Strength meter — fair |
| `--color-wk-success` | Strength meter — strong |
| `--size-wk-sm` / `md` / `lg` | Input height per size variant |
| `--radius-wk-md` | Border radius |
| `--transition-wk-duration` | Toggle + meter transition speed |

## Customization

Override defaults in `config/wirekit.php`:

```php
'components' => [
    'password-input' => ['size' => 'md', 'toggle' => true, 'strengthMeter' => false],
],
```

## Further Reading

- [MDN: `<input type="password">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/password)
- [MDN: `role="meter"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/meter_role)
- [MDN: `aria-pressed`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed)
- [WAI-ARIA: Password Best Practices](https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/)
- [WebAIM: Creating Accessible Forms](https://webaim.org/techniques/forms/)
