Conditional Visibility
Conditional visibility lets one field or fieldset mount or unmount based on another field's current value in the browser.
use InertiaUI\Forms\Fields\Fieldset;
use InertiaUI\Forms\Fields\Combobox;
use InertiaUI\Forms\Fields\TextInput;
return [
Combobox::make('contact_method')
->options([
'email' => 'Email',
'phone' => 'Phone',
]),
TextInput::make('phone_number')
->visibleWhen('contact_method', 'phone'),
TextInput::make('email_notes')
->hiddenWhen('contact_method', 'phone'),
TextInput::make('contact_details')
->visibleWhen('contact_method', operator: 'not_empty'),
Fieldset::make()
->legend('Phone follow-up')
->visibleWhen('contact_method', 'phone')
->fields([
TextInput::make('phone_extension'),
]),
];For in-list checks, prefer the fluent helpers over passing the operator manually:
TextInput::make('vat_id')
->visibleWhenIn('country', ['NL', 'BE', 'DE']);
TextInput::make('domestic_notes')
->hiddenWhenIn('country', ['NL', 'BE', 'DE']);Operators
Beyond matching an exact value, visibleWhen() and hiddenWhen() accept an operator to compare against. The available operators are:
| Operator | Meaning |
|---|---|
= | Visible when the referenced value matches. |
!= | Visible when the referenced value does not match. |
> | Visible when the referenced value is numerically greater than the configured value. |
>= | Visible when the referenced value is numerically greater than or equal to the configured value. |
< | Visible when the referenced value is numerically less than the configured value. |
<= | Visible when the referenced value is numerically less than or equal to the configured value. |
in | Visible when the referenced value appears in the configured array. |
not_in | Visible when the referenced value does not appear in the configured array. |
contains | Visible when the referenced array or string contains the configured value. |
empty | Visible when the referenced value is null, undefined, an empty string, or an empty array. |
not_empty | Visible when the referenced value is not empty. |
truthy | Visible when the referenced value is truthy. Empty values are treated as false. |
falsy | Visible when the referenced value is falsy. Empty values are treated as false. |
For comparisons, named arguments keep the condition readable:
TextInput::make('guardian_name')
->visibleWhen('age', operator: '<', value: 18);The field path may be a dotted path such as address.country.
Numeric comparisons convert numbers and numeric strings explicitly. Non-numeric values do not match numeric comparison operators. Equality, in, and array contains use the same numeric normalization, so 10 and '10' match.
The empty, not_empty, truthy, and falsy operators do not serialize a value.
Paths Inside Repeaters and Blocks
A Repeater holds a list of rows, so the same field exists once per row. Inside a repeater schema, a field path resolves against the row it is rendered in, not the whole form. Given this form data:
[
'contact_method' => 'phone', // top-level field
'contacts' => [
['type' => 'business', 'company_name' => 'Acme'], // row 0
['type' => 'personal', 'company_name' => null], // row 1
],
]visibleWhen('type', 'business') reads the type of its own row. Row 0 shows company_name and row 1 hides it, because each row evaluates the condition against its own values:
Repeater::make('contacts')
->schema([
Combobox::make('type')->options([
'personal' => 'Personal',
'business' => 'Business',
]),
TextInput::make('company_name')
->visibleWhen('type', 'business'),
]);To read a field outside the row, prefix the path with $.. This condition looks at the top-level contact_method, so it resolves the same way for every row:
TextInput::make('phone_follow_up')
->visibleWhen('$.contact_method', 'phone'),$. always means the outermost form data. In a repeater nested inside another repeater, it still reaches the top-level form rather than the parent row.
Blocks set schemas work the same way: a plain path reads the current block's own data, and $. reads the top-level form data.
Groups
Multiple chained visibleWhen() calls are serialized as an and group. For explicit grouping, use visibleWhenAll(), visibleWhenAny(), and visibleWhenNot():
TextInput::make('business_phone_note')
->visibleWhenAll(fn ($visibility) => $visibility
->where('contact_method', 'phone')
->where('customer_type', 'business'));
TextInput::make('priority_contact_note')
->visibleWhenAny(fn ($visibility) => $visibility
->where('contact_method', 'phone')
->where('customer_type', 'business'));
TextInput::make('non_business_note')
->visibleWhenNot(fn ($visibility) => $visibility
->where('customer_type', 'business'));Use a grouped helper only for the part that needs grouping. This field is visible for team-plan accounts unless the account is suspended or billing is overdue:
Textarea::make('renewal_note')
->visibleWhen('plan', 'team')
->visibleWhenNot(fn ($visibility) => $visibility
->where('account_status', 'suspended')
->where('billing_status', 'overdue'));The first call is a normal condition. The visibleWhenNot() group evaluates false when any child condition matches, so both blocked states must be absent for the field to render.
Each root visibility configuration includes an inferred dependsOn array containing the referenced field paths. It is metadata only; the current frontend renderers already evaluate visibility reactively from form data and do not require extra renderer configuration.
Behavior
Hidden fields and fieldsets are removed from the DOM by the Vue and React renderers, so their controls leave the tab order and accessibility tree. Their values are retained in Inertia form data unless clearWhenHidden() clears them.
Call clearWhenHidden() when a field should clear its client-side value as it transitions from visible to hidden:
TextInput::make('phone_number')
->visibleWhen('contact_method', 'phone')
->clearWhenHidden();The same method on a fieldset clears every submitted child when the fieldset transitions from visible to hidden:
Fieldset::make([
TextInput::make('phone_extension'),
TextInput::make('phone_notes'),
])
->visibleWhen('contact_method', 'phone')
->clearWhenHidden();This is opt-in. Fields without clearWhenHidden() keep their current value when hidden. Initial hidden fields are not cleared on mount; clearing only happens after a visible field becomes hidden in the browser. Clearing also removes validation errors for that field.
Built-in fields clear to the same empty shape used by their controls:
Checkboxuses its configuredfalseValue, andToggleuses its configuredoffValue.CheckboxGroup,Repeater, andBlocksuse[].KeyValueuses[]insinglemode and{}inkeyedmode.Comboboxuses[]for tags and multiple selections, andnullfor a single choice or record.TextInput,Slug,Textarea,Hidden,OtpInput, andComposeruse an empty string.- A plain
Linkuses an empty string. A structured link uses{ url: '' }and also includes emptylabelortargetkeys when those options are enabled. - Other built-ins and custom fields use
nullunless their renderer defines a more specific shape.
Validation
Inertia Forms evaluates the same visibility conditions against submitted data when a request is attached. Hidden fields receive Laravel's exclude validation rule, so their own rules are skipped and they are omitted from validate() / validated() output. Hidden fieldsets exclude each child field the same way. Repeater rows and block items are evaluated per submitted index, so one row may require a field while another row excludes it. A required rule therefore means required when visible. Precognitive validation uses the same server exclusion before the existing Precognition-Validate-Only error filtering runs.
Wizards
In wizards, steps whose fieldset is hidden or whose fieldset has no currently visible fields are omitted from browser step navigation. Visibility changes keep the current step index within the visible step list.
Limits
Editor internals and file upload internals do not get nested visibility semantics from this feature.
Visibility Is Not Authorization
Conditional visibility is presentation logic, not access control. A field hidden by visibleWhen() or hiddenWhen() is still part of the serialized form so the browser may react to form data. Do not use visibility for permissions or secrets.
Use authorizedWhen() for fields that should not be sent to the frontend for the current user or request. Authorization runs before visibility, so unauthorized fields are removed before the frontend evaluates visibility rules.
clearWhenHidden() only clears a client-side value after a visible field becomes hidden. Authorization removes the field from the initial data and skips its validation rules before the browser receives the form.