Currently Available: Need a skilled Software Developer for your next project?
Categories
Laravel

How to Build Dependent Category and Subcategory Select Fields in Filament

In a Filament resource form, a dependent select limits the choices in one field based on another field. For example, a subcategory select should show only subcategories that belong to the selected category.

Configure the category field to refresh the form when its value changes and clear the selected subcategory. Configure the subcategory field to show only records linked to the selected category. Filament forms use Livewire, which sends field updates and refreshes the relevant form state. A field's live() method turns on those updates. The example below uses Filament 3 and assumes the form’s model has category and subcategory relationships.

Define the relationships and dependent selects

The model behind the form needs relationships that match the select fields. A category has many subcategories, and the record being edited belongs to one category and one subcategory. The record should store the IDs that link it to those records, called foreign keys, such as category_id and subcategory_id.

In the resource form schema, define the selects like this:

use Filament\Forms\Components\Select;
use Filament\Forms\Get;
use Filament\Forms\Set;
use Illuminate\Database\Eloquent\Builder;

Select::make('category_id')
    ->relationship(name: 'category', titleAttribute: 'name')
    ->live()
    ->afterStateUpdated(function (Set $set): void {
        $set('subcategory_id', null);
    }),

Select::make('subcategory_id')
    ->relationship(
        name: 'subcategory',
        titleAttribute: 'name',
        modifyQueryUsing: fn (Builder $query, Get $get): Builder =>
            $query->where('category_id', $get('category_id')),
    )
    ->disabled(fn (Get $get): bool => blank($get('category_id')))
    ->placeholder(fn (Get $get): string => blank($get('category_id'))
        ? 'Choose a category first'
        : 'Select a subcategory'),

The category select calls live() so Filament refreshes the form when the user changes its value. Its afterStateUpdated callback clears subcategory_id, preventing a subcategory from the previous category from remaining selected.

The subcategory select filters its relationship query using the current category_id, which Filament provides through the injected $get helper. It stays disabled until the user selects a category, and its placeholder tells the user what to do next. Filament’s advanced forms documentation describes live field updates and injected form utilities.

Check the form when editing a record

An edit form must show the category and subcategory already saved on the record. Selects that use relationship() give Filament a model relationship to load the saved category and subcategory values. If a custom options() callback replaces relationship(), check that the form has both saved values available: the child options depend on the parent value, so an empty parent state leaves the child with no matching choices.

For a form that uses options(), set the category and subcategory from the record when the category field has no value. A Filament edit-form example shows this pattern with $record and $set. After making the change, test both opening an existing record and changing its category. The first test confirms the saved selections appear; the second confirms the old subcategory clears and the new choices match the selected category.

Choose relationship queries for related records

Use relationship() with modifyQueryUsing() when the choices come from related database records. Filament then queries the relationship with the category condition, while the form field remains connected to the model relationship. This approach also fits when a select must save the selected related record, rather than only display a list of choices.

An options() callback can work for a simple list, but it only supplies choices. You must also ensure the selected value is saved to the intended field or relationship. For dependent table filters, Filament maintainers have reported that options() can delay updates to the parent filter's value; the table-filter issue describes that behavior and notes that a relationship query updates in real time. Table filters and resource form fields are different contexts, so test the version and component you use.

The cited form examples cover Filament 3, while the table-filter report concerns Filament 4. Check the documentation for the Filament version installed in your project before copying APIs across versions.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *