parfaitementweb/filament-password-input
An enhanced password input component for Filament that adds a reveal-password toggle, copy-to-clipboard button, and a generate-new-password action. Drop-in replacement for Password::make() with no config or migrations required.
Install it and use it — there is genuinely nothing else to do:
composer require parfaitementweb/filament-password-input
No config file, no migration, no asset build. The only optional publish step is translations, and only if you want to change the tooltips/messages:
php artisan vendor:publish --tag=filament-password-input-translations
The class is a drop-in replacement for Filament\Forms\Components\TextInput used with ->password(). Import it, alias it if you already import a Password class, and swap it into a schema:
use Parfaitementweb\FilamentPasswordInput\Password;
use Filament\Schemas\Schema; // Filament 4+; Filament 3 uses Filament\Forms\Form
public function form(Schema $schema): Schema
{
return $schema->schema([
Password::make('password')
->label('Password')
->required()
->maxLength(255),
]);
}
The reveal toggle is on by default; ->revealable(false) gives you back a stock password input. Your first real use case should be the create-user form in your UserResource — that is where all three features (reveal, generate, copy) earn their keep for an admin who has to hand a password to a colleague.
Where to look first: the README's "Kitchen Sink" example (all actions at once), then the Password class itself — it is a thin subclass, so reading it takes two minutes and answers most questions the README leaves implicit.
Create/edit resource split. Passwords are write-only in practice. Combine the package with stock Filament dehydration so the edit form doesn't blank the hash:
Password::make('password')
->required(fn (string $operation) => $operation === 'create')
->dehydrated(fn (?string $state) => filled($state))
->dehydrateStateUsing(fn (string $state) => Hash::make($state))
->copyable(color: 'warning')
->regeneratePassword(color: 'primary')
->inlineSuffix(),
Keep inlineSuffix() on dense admin tables; drop it when the input is narrow and the buttons would eat the text.
Admin onboarding flow. The strongest pattern is generate → copy → hand over. regeneratePassword() fires a Filament notification by default ("New password was generated!"); on a shared screen you may prefer ->regeneratePassword(notify: false) and instead show your own copy-to-clipboard prompt. Since the copy button reads the current input value, generate-then-copy in the same session is the intended workflow.
Global defaults in a service provider. Anything you'd otherwise repeat on every field goes in AppServiceProvider::boot():
Password::configureUsing(function (Password $password) {
$password->maxLength(32)->copyable(color: 'gray');
});
Per-field calls always win over configureUsing, so treat it as a baseline, not a policy. Note this only affects fields using this class — existing TextInput::make('password')->password() fields across your app are untouched until you swap them.
Icons. Register aliases once, usually in the same provider:
FilamentIcon::register([
'filament-password-input::copy' => 'heroicon-o-clipboard',
'filament-password-input::regenerate' => 'heroicon-o-key',
]);
The reveal/hide aliases (forms::components.text-input.actions.show-password / hide-password) are Filament's own — changing them affects every revealable text input in the app, not just this package.
Custom generators. When you have a house password policy (e.g. no ambiguous characters), pass a closure; Filament's usual utility injection applies:
->regeneratePassword(using: fn () => Str::password(16, symbols: false))
->newPasswordLength(16)
Pairing with confirmation fields. A generated password lands in the input via Livewire and does not touch password_confirmation. If you validate confirmed, mirror it yourself:
Password::make('password')
->regeneratePassword()
->afterStateUpdated(fn (Set $set, $state) => $set('password_confirmation', $state))
->confirmed(),
Verify this on your Filament version — the regenerate callback is server-driven, so afterStateUpdated normally fires, but confirm in a Livewire test rather than assuming.
Secrets outside users. The component is not user-specific. API tokens, one-time invite codes, SMTP passwords in a settings page, or a "view generated credential" action modal all benefit from copyable() + revealable(). For read-only display, use ->readonly() (copy works) rather than ->disabled().
Testing. It behaves as a normal Filament field, so fillForm(['password' => 'secret']) and assertFormSet keep working. For the generate button, assert the state changed rather than the exact value, and assert your confirmation mirroring explicitly.
copyable() is hidden on disabled() fields but shown on readonly(). Getting this wrong is the #1 "the feature doesn't work" report.disabled() and readonly(). Read-only records get copy only — which is the correct behaviour for an edit form showing an existing hash.copyable() on an edit form for an existing user. The input is empty (you can't reveal a bcrypt hash), so the button copies nothing. It belongs on create/invite/password-reset flows.Str::password() needs a minimum length of 3. ->maxLength(2) with the default generator will throw. Laravel's helper is the default source of the generated value — know its constraints before tightening maxLength.maxLength() vs newPasswordLength(). The default generator uses maxLength() when present (32 otherwise); newPasswordLength() sets an explicit length. Setting both is ambiguous — pick one per field.copyMessageDuration() is milliseconds, integer only. ->copyMessageDuration(3) gives you a 3ms tooltip, not 3 seconds.filament::components/copyable.messages.copied changes every copyable component in your app; prefer ->copyMessage() per field unless you truly want it global.hidePasswordManagerIcons() adds data-1p-ignore and data-lpignore="true"; 1Password and LastPass mostly respect it, browser-native managers may not. Use it on non-password semantics (e.g. API key fields) rather than expecting a hard guarantee.form() signature to form(Schema $schema): Schema. The README still shows the Filament 3 Form variant — adapt copied snippets or you'll chase a type error.StrongPassword field, extend Parfaitementweb\FilamentPasswordInput\Password, set defaults in the constructor or setUp(), and register it as your own form component.disabled()/readonly() closures first (they receive $record, so ->disabled(fn ($record) => $record !== null) disables on edit), then whether you accidentally overwrote suffixActions — the package merges into the existing suffixActions, so custom suffix actions and its buttons coexist rather than replace.How can I help you explore Laravel packages today?