Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

raobilal4822's avatar

"Filament: Checkbox re-checking after form submit despite user unchecking (with afterStateHydrated)"

I'm using Laravel Filament v3 and have a Checkbox::make('add_guarantee') field that is not saved directly to the database (i.e., it's only used to conditionally control another field, guarantor_id).

The logic I want:

  • When editing a record, if guarantor_id is already set in the database, the checkbox should be pre-checked.
  • If the user unchecks the checkbox and submits the form, guarantor_id should be cleared (set to null).
  • The checkbox itself should NOT be saved to the database (dehydrated(false)).
  • The checkbox should not get re-checked automatically after submission or form re-render if the user had unchecked it.

Here's the current code:

0 likes
1 reply
LaryAI's avatar
Level 58

Certainly! This is a common scenario when using Filament forms with "virtual" fields that are not persisted, but need to be initialized based on the record. The core of your problem is that afterStateHydrated (and even default) will run on every hydration, not just the initial mount, and $record may not reflect the latest form data after submission.

The recommended Filament approach is to use the default() method for initial state, and avoid using afterStateHydrated for this purpose.
However, you must ensure that the default only applies on the very first load, and does not override user input on subsequent hydrations.

Solution

Use default() with a closure, and remove afterStateHydrated.
Filament will only use the default() value if there is no value in the form state (i.e., on first load or when creating a new record).
After the user interacts with the checkbox, their input will be respected, and the checkbox will not be re-checked on subsequent renders.

Example:

Checkbox::make('add_guarantee')
    ->label('Add Guarantee')
    ->reactive()
    ->live()
    ->dehydrated(false)
    ->default(fn ($record) => !blank($record?->guarantor_id))
    ->afterStateUpdated(function ($state, $set) {
        if (!$state) {
            $set('guarantor_id', null);
        }
    }),

Explanation:

  • default(fn ($record) => !blank($record?->guarantor_id))
    This will pre-check the checkbox only on the initial load if the record has a guarantor_id.
    After the user interacts with the checkbox, their input is preserved in the Livewire state, and the default is ignored.
  • Remove afterStateHydrated entirely.
    It is not needed for this use case and causes the re-checking issue.
  • dehydrated(false) ensures the checkbox is not saved to the database.
  • afterStateUpdated clears guarantor_id when the checkbox is unchecked.

Why default works (and afterStateHydrated does not)

  • default only sets the initial value if the field is not already set in the Livewire state.
  • afterStateHydrated runs on every hydration, including after form submission, and can override user input.

If you need to support "reset" actions

If you have a "reset" button or similar, and want to re-initialize the checkbox, you may need to handle that separately (e.g., by resetting the form state).


Summary:

  • Use default for initial state.
  • Remove afterStateHydrated.
  • Your checkbox will now only be pre-checked on first load, and will respect user changes after that.

Let me know if you need further clarification!

Please or to participate in this conversation.