Back to Course |
Filament 3 From Scratch: Practical Course

Dynamic Forms: Hidden, Disabled and "Reactive" Options

It seems like we know all about forms in Filament, but we actually just scratched the surface of possibilities in this course. The real power comes with the dynamic behavior of form fields. We will explore a few examples in this lesson.


Example 1. Auto-Generate Slug from Title

Suppose we have two DB fields: products.name and products.slug. And we want the slug to be auto-generated in the form as we're typing the name.

Typical Resource form:

app/Filament/Resources/ProductResource.php

return $form
->schema([
Forms\Components\TextInput::make('name')
->label(__('Product name'))
->required()
->unique(ignoreRecord: true),
Forms\Components\TextInput::make('slug')
->required(),

Now, how do we make this form reactive? We need to add two things to the "name" column:

  • Specify that it's ->live(), which means that the form is refreshed after its value change
  • Specify the afterStateUpdated() logic and what happens with that value

The code looks like this:

return $form
->schema([
Forms\Components\TextInput::make('name')
->label(__('Product name'))
->required()
->unique(ignoreRecord: true)
->live(onBlur: true)
->afterStateUpdated(fn (Forms\Set $set, ?string $state) => $set('slug', str()->slug($state))),
Forms\Components\TextInput::make('slug')
->required(),

So, the main action is in the callback function: we set the slug value to whatever we want from the initial $state variable value.

The important thing is a parameter in the live(onBlur: true). If you don't specify it, the form will auto-refresh while typing with every symbol, which is not a pleasant UX behavior, as it would keep overwriting the values. The onBlur would refresh the form only when you finish typing and click "Tab" or click away from the "name" input field.


Example 2. Disable Field in Edit Form

Let's continue our example of the slug field by making it disabled in the Edit form.

As usual, we need to add a method to the input chain. One of the possible methods is ->disabled(true/false). But the trick here is to add a condition as a parameter that should return true/false: WHEN it should be disabled.

Filament offers a better method called ->disabledOn() with the parameter of the page(s) to be disabled.

So here's our code:

Forms\Components\TextInput::make('slug')
->disabledOn('edit')
->required(),

And that's it. Slug cannot be edited!

Similarly, Filament offers fields to be visible/hidden on certain pages:

Forms\Components\TextInput::make('slug')
->hiddenOn('edit')
->required(),
 
// Or the opposite:
Forms\Components\TextInput::make('slug')
->visibleOn('create')
->required(),

Example 3. Radio Options Dynamic Based on Other Fields

This example will be not from our course demo project but from another Filament tutorial about appointment reservation.

Imagine a form where you choose a date, and the system shows you available times that are not booked yet.

This is the code of the Resource:

ReservationResource.php:

public static function form(Form $form): Form
{
return $form
->schema([
DatePicker::make('date')
->required()
->live(),
Radio::make('track')
->options(fn (Get $get) => self::getAvailableReservations($get))
->hidden(fn (Get $get) => ! $get('date')),
]);
}
 
public static function getAvailableReservations(Get $get): array
{
$date = Carbon::parse($get('date'));
 
$availableReservations = [];
 
// ... logic of getting available times from DB
 
foreach ($availableTimes as $time) {
$key = $track->id . '-' . $time->format('H');
$availableReservations[$key] = $track->title . ' ' . $time->format('H:i');
}
 
return $availableReservations;
}

Here's what you need to understand here:

  1. The date field is made ->live() for reactivity
  2. Radio track options are dynamic, with the $get variable containing all the form field values for that moment, including the $get('date') that we need
  3. We have a separate method getAvailableReservations() to get the reservations in key-value format. By the way, the method should be static, as the form() method is static, too.
  4. Similarly, we have a ->hidden() method for track options: it is shown only if the date is chosen