Skip to content

Conditional Visibility

Conditional visibility lets one field or fieldset mount or unmount based on another field's current value in the browser.

Example
php
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:

php
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:

OperatorMeaning
=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.
inVisible when the referenced value appears in the configured array.
not_inVisible when the referenced value does not appear in the configured array.
containsVisible when the referenced array or string contains the configured value.
emptyVisible when the referenced value is null, undefined, an empty string, or an empty array.
not_emptyVisible when the referenced value is not empty.
truthyVisible when the referenced value is truthy. Empty values are treated as false.
falsyVisible when the referenced value is falsy. Empty values are treated as false.

For comparisons, named arguments keep the condition readable:

php
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:

php
[
    '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:

php
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:

php
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():

php
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:

Example
php
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:

php
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:

php
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:

  • Checkbox uses its configured falseValue, and Toggle uses its configured offValue.
  • CheckboxGroup, Repeater, and Blocks use [].
  • KeyValue uses [] in single mode and {} in keyed mode.
  • Combobox uses [] for tags and multiple selections, and null for a single choice or record.
  • TextInput, Slug, Textarea, Hidden, OtpInput, and Composer use an empty string.
  • A plain Link uses an empty string. A structured link uses { url: '' } and also includes empty label or target keys when those options are enabled.
  • Other built-ins and custom fields use null unless 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.