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

raobilal4822's avatar

"FilamentPHP Checkbox: Hydration vs Default Issue with Linked Field Behavior"

I’m working in a Laravel + FilamentPHP project. I have a checkbox named add_guarantee which is not tied directly to the database, but it controls the presence or absence of another model field: guarantor_id.

If the guarantor_id exists in the $record, then the checkbox should be checked by default on the edit page. If I uncheck it, the guarantor_id should be set to null.

I’m using this setup to manage that logic:

But in this case, the checkbox is not checked on edit page even if the guarantor_id is present.

From my understanding:

default() doesn't apply correctly because the form already has a state on edit page.

afterStateHydrated() overrides any interaction if I don’t condition it properly.

🔍 I want a robust way to:

Show the checkbox as checked if guarantor_id exists.

Respect the user’s changes (don’t re-check it automatically).

Clear guarantor_id if user unchecks it.

Avoid using mount() method because I want to keep the logic inside Filament fields.

✅ Please suggest the best practice or an optimized approach to solve this within Filament's checkbox component logic.

0 likes
1 reply
LaryAI's avatar
Level 58

Absolutely, this is a common scenario when dealing with "virtual" fields in FilamentPHP forms. The core issue is that afterStateHydrated runs every time the form is hydrated (including after submit/validation), so it can override user input if not carefully conditioned. Meanwhile, default() only applies on creation, not on edit.

Here’s a robust approach that solves all your requirements:

  • Show the checkbox as checked if guarantor_id exists.
  • Respect the user’s changes (don’t re-check it automatically).
  • Clear guarantor_id if user unchecks it.
  • Keep logic inside the Filament field definition.

Solution

The trick is to only set the checkbox state in afterStateHydrated if the user hasn’t already interacted with the field. Filament provides a way to check if the state is already set (i.e., the user has interacted or the form has been submitted).

Here’s the recommended setup:

Checkbox::make('add_guarantee')
    ->label('Add Guarantee')
    ->reactive()
    ->live()
    ->dehydrated(false) // Don't persist this field
    ->afterStateHydrated(function ($component, $state, $record) {
        // Only set the state if it is null (i.e., first hydration)
        if (is_null($state)) {
            $component->state((bool) ($record?->guarantor_id));
        }
    })
    ->afterStateUpdated(function ($state, $set) {
        if (!$state) {
            $set('guarantor_id', null); // Clear related field when unchecked
        }
    }),

Explanation

  • afterStateHydrated:
    • Only sets the checkbox state if it is null (meaning the user hasn’t interacted yet, and the form hasn’t been submitted/validated).
    • This ensures the checkbox is checked if guarantor_id exists, but only on the initial load.
  • afterStateUpdated:
    • When the checkbox is unchecked, it sets guarantor_id to null.

Why not use default()?

  • default() only applies on create, not on edit.
  • On edit, the form state is already set from the record, so default() is ignored.

Why not always set state in afterStateHydrated?

  • Because it would override user input after validation errors or when the form is rehydrated for any reason.

Summary:
This approach ensures the checkbox is checked on edit if guarantor_id exists, but after the user interacts, their choice is always respected.

Let me know if you need a full field group example or further clarification!

Please or to participate in this conversation.