eg-mohamed/filament-otp-input
A Filament form component that adds a one-time password (OTP) input field to your admin panels. Users can enter verification codes with a clean, segmented input UI, making it easy to integrate OTP or PIN confirmation into Filament forms and workflows.
filament-otp-input provides a OtpInput form field for Filament panels (v3/v4 line) that renders a segmented one-time-password entry control instead of a plain text input. Version 4.2.0 is a minor release on the 4.x line — the changelog entry published for it is a bare compare link, so treat the upgrade as a routine composer update with no migration work, but skim the diff before bumping in a large panel.
Minimal path:
composer require eg-mohamed/filament-otp-input (pin ^4.2 once verified).public/, run php artisan filament:assets after install and after every upgrade — the field ships Alpine/JS behaviour that is published, not injected at runtime.use EG\FilamentOtpInput\Forms\Components\OtpInput;
OtpInput::make('code')
->length(6)
->required();
First realistic use case: a two-step login challenge. Put the OTP field on a dedicated Filament page or a secondary step in an existing login flow, where step one collects credentials and step two verifies a code mailed or texted to the user.
Look first at the field class itself (Forms/Components/OtpInput.php) — the public fluent API is small and is the fastest way to discover the exact method names for your installed tag, since the README occasionally lags behind.
Two-step login / 2FA challenge. Keep the password step and the OTP step as separate Livewire components or separate steps in a wizard, so the OTP field mounts fresh (and auto-focuses) after the challenge is issued. Store the pending user id and expiry in the session, not in the form state.
Pair with Filament's wizard or Action modal. For "verify phone number" inside a resource page, use a Action with a form schema containing the OTP field plus a resend link rendered via ->extraAttributes() or a placeholder. This keeps verification logic scoped to the record being edited.
Validation lives on the field. Align your rules with the rendered length:
OtpInput::make('code')
->length(6)
->rules(['required', 'digits:6'])
->validationMessages(['digits' => 'Enter the 6-digit code.']);
->required() alone does not enforce an exact digit count — always add digits:N or size:N.
Auto-submit. If the component supports auto-advance/auto-submit, wire the final box to call the form's submit action rather than duplicating button logic. Guard against double submission with a $this->otpSubmitting flag or by disabling the submit button while the verify call is in flight.
Resend cooldown. Model resend as a rate-limited action (RateLimiter::attempt keyed on user id + purpose) with a countdown in the UI. The OTP field is presentation only; expiry and attempt-count enforcement belong in your service class.
Testing. In Pest/PHPUnit Livewire tests, set the field as a plain string — ->fillForm(['code' => '123456'])->call('submit') — because the segmented UI is a client-side representation of a single string state. Assert both the success path and an expired-code path.
php artisan filament:assets and clear view:clear / config:clear after moving to 4.2.0. Stale published JS against a new field class is the most common "it renders but doesn't advance" report.array in your ->action() closures or form callbacks.(string) before comparing against a stored code hash, and avoid loose == against ints.wire:model.live on every keystroke.aria description, since segmented inputs frequently break assistive tech.Field, so ->extraInputAttributes(), ->extraAttributes(), ->helperText(), ->hint(), ->disabled(), and ->live() compose as expected. Reach for these before subclassing.composer show eg-mohamed/filament-otp-input and a quick read of the class. Pin the minor version in composer.json until you've eyeballed the diff.How can I help you explore Laravel packages today?