Summary
Select one value or build a searchable multiselect with chips, checklists, images and descriptions. Large local catalogs use virtualized lists; remote catalogs can load one page at a time. On small screens, the dropdown becomes a bottom sheet.Choose the presentation
Sizes, surfaces, rich rows, grids and custom views.
Work with large catalogs
Local virtualization or paginated server search.
Save valid selections
Allowed values, limits, relationships and database casts.
Works with all standard Filament field APIs:
required(), disabled(), hidden(), live(), afterStateUpdated(), validation rules, etc.
This guide covers the FLEX-specific configuration methods and practical Select workflows. Shared form, schema and action methods remain available through inheritance; see the Filament Select reference for the wider inherited API.
Basic usage
Standard select with rich options
Multi-select with chips
Email recipients (two-line options + chip labels)
UsechipLabel (or chip_label) on rich options when the dropdown should show name + email, but selected chips should show only the email:
chipLabel text.
Custom value (optionView())
Use optionView() when you need full control over option HTML — custom Blade per row and trigger (server-side equivalent of render props). Pair with optionTriggerView() when the closed trigger should differ from the dropdown row.
$option, $layout (list, trigger, grid, chip), $field, $value, $label, $description, and any data from optionViewData().
Here the custom views can read $showEmail. Pass a closure to optionViewData() when that extra data depends on the current record or form. Your custom view is responsible for displaying its own markup; keep application text escaped with Blade’s {{ }} output.
Use richListTriggerDisplay() to keep the full list row (avatar + name + email) in the closed trigger instead of the compact trigger layout.
Htmlable, View, or a nested view name:
optionView() automatically enables allowHtml() and sanitizes output through the package HTML sanitizer.
Rich relationship options
UsegetRichOptionFromRecordUsing() to return a rich option array for an Eloquent relationship. FLEX renders the dropdown, search results and saved selection without application calls to transformRichOptionsForJs():
id are supported; the option value follows the native relationship key. Cached searches retain the same rich metadata after rendering a selected value.
The callback accepts the same rich shape as options(), including icons, images, badges and chip_label. It can be configured before or after relationship(). Default selected labels remain compact; richListTriggerDisplay() preserves the full list row. Text remains escaped unless you explicitly enable allowHtml().
This is an alternative to the string-returning getOptionLabelFromRecordUsing() callback. Relationship query constraints, validation and persistence remain owned by Filament and the application. Existing getOptionLabelUsing() callbacks still return strings.
Multi-select checklist (keep options visible)
UsekeepSelectedOptionsInDropdown() when selected options should stay in the list with checkmarks (dropdown stays open while toggling):
State & validation
Stored value
State is the option key fromoptions().
Default state
The field defaults tonull (single) or [] (multiple).
Validation rules
Configuration API
The signatures below show which settings accept closures. Closures use Filament’s utility injection where supported.variant()
optionLayout()
Use grid for a multi-column visual picker:
inlineSearch()
Recommended for single-select searchable fields to keep the UI compact — the search field is the trigger, not a separate label swap:
multiple(): hasInlineSearch() is false when the field is multi-select, even if you chain inlineSearch(). Multi-select fields should use dropdown / sheet header search (searchable() without relying on inlineSearch()).
For RTL layouts, set extraAttributes(['dir' => 'rtl']) on the field — the teleported panel and mobile sheet (bottom drawer) both copy the trigger writing direction onto the menu (dir="rtl"), so search icons, checkmarks (inset-inline-end), and option text mirror correctly. Search inputs use dir="auto" so Hebrew/Arabic queries get an RTL caret while Latin queries keep a normal LTR caret.
Works with entityMentions() — type @ in the inline search input or press @ on the closed trigger to start a mention query.
Field label, accent and rounding
Keep the label inside the field and choose its surface, accent and corner shape independently:inlineFieldLabel(false) restores the outer label. color(null) restores the default accent. rounding(null) uses the global ui.field_rounding setting; rounding('full') creates a pill. The outer focus outline is enabled by default; focusOutline(false) disables it.
Secondary text beside the option label
below places the description beneath the label. trailing puts it at the logical end of the row, including RTL layouts. This configures the shared rich-option renderer; custom optionView() markup controls its own layout.
Smart suggest (recentOptions, suggestedOptions, allowCreateOption)
Pin curated rows at the top of the dropdown and optionally allow creating a new value from the current search:
searchable(). When the query has no exact label match, a Create “…” row appears at the top of the list (label from filament-flex-fields::default.select_field.smart_suggest.create). Choosing that row commits the trimmed search string as the field value — same string for both value and label. No modal opens and no PHP callback runs at click time.
With allowCreateOption() enabled, a new text value does not have to match an existing option. The server still validates the selection structure. Add application rules for the text and its length:
Persist created values after your form has been validated. Inline creation changes the field value; it does not insert a database record. This example limits each label and the number of selections:
Create option: inline vs modal (which API?)
Choose inline creation for a text value, or a modal for a record with its own fields and validation:- Name only
- Additional fields
A create modal does not require extra fields. This example asks only for a name, validates it and returns the new author’s key. The model must allow creation with only
Author is your application’s model.name (including any required database columns). The newly created record becomes the selected option.Entity mentions (entityMentions())
Async people/entity picker triggered with @ in the closed trigger, inline search input, or dropdown search:
getOptionLabelsUsing() to resolve saved selections in a multiselect, or getOptionLabelUsing() for a single selection.
Real-world examples
User select with avatars
Multi-select tags in a Section
Grouped sections
Nest option groups inoptions() to render section headers in the dropdown. Enable explicit dividers between groups (default on):
optionGroupSeparators(false) to fall back to header-only dividers.
Disabled options
Disable specific keys while keeping them visible in the list:disableOptionWhen() remains available for dynamic rules.
Async search with load more
For large remote datasets, paginate search results and append rows as the user scrolls:CharacterRepository::search() in your application. Each item contains a stable value and its display label. Rich rows may also contain description, image, icon and disabled. For example, the final page can return:
hasMore => true and a nonempty next cursor. Keep that cursor opaque, stable and advancing; never return the previous cursor. Return at most $pageSize option records. Group records may use ['label' => 'Group name', 'options' => [...]]; their option leaves share the same page-size limit. The selected-label resolver must authorize and resolve the same catalog. The trigger shows a loading indicator while the first page loads or while a new query is debounced. A footer spinner appears while additional pages load.
Closure-based options() are resolved when the menu opens. A saved selection may need a label before opening: provide getOptionLabelUsing() or getOptionLabelsUsing() rather than fetching an entire catalog just to display its selected value.
Pagination note: true “load more” paging requiresgetSearchResultsPageUsing(). Without it, the field may still call a non-paginated search callback and will sethasMore=falseafter the first page so the client does not spin forever.
Search rate limit: Livewire search endpoints for Select / Tags / IconPicker are capped byfilament-flex-fields.select.search_rate_limit_per_minute(default 60/min per actor+field). The limiter keys on the authenticated user id when present, otherwiseRequest::ip()— configure Laravel TrustedProxies correctly behind a load balancer so spoofedX-Forwarded-Forheaders are not trusted.
FlexFieldFormBuilder / Studio config
SelectFieldConfigurator (Studio / FlexFieldFormBuilder) maps these config keys onto the fluent API:
PHP-only (not configured from Studio): async search (
getSearchResultsUsing(), getSearchResultsPageUsing(), paginatedSearchResults()), relationship(), dependsOn(), create/edit option modals, and other Livewire search contracts. Wire those on the field in PHP.
variant('item-card') defaults clearable off unless clearable is set explicitly in config or via clearable().
Playground
/admin/flex-fields/select-field
Open the live Select demo, or follow Installation to enable the local playground.
Filament Select parity
SelectField extends Filament\Forms\Components\Select. Every public Select API from the Filament docs is available with the same method names. The headless Alpine/Livewire UI wires behaviour that Filament’s JS select would otherwise handle.
Out of scope for this field (separate Filament components): MorphToSelect, ModalTableSelect. Use those classes when you need morph type pickers or table-in-modal selection.
Coverage matrix (Filament Select docs → SelectField)
Basic options & JS select
Searching & custom messages
Every searchable dropdown receives a translated placeholder by default (Start typing to search... in English). Use searchPrompt() to replace the search text. placeholder() separately controls the closed field when no option is selected; it does not replace the dropdown’s search placeholder.
inlineSearch(), including the mobile search header. A closure may supply a context-dependent prompt. An explicitly empty searchPrompt('') hides the text intentionally.
To search by keys as well as labels:
Multi-select, reorder, min/max items
getOptionLabelsUsing() (plural), same as Filament.
Grouped options
Relationship (+ preload, create/edit modals, pivot)
allowCreateOption()) is documented under Create option: inline vs modal: one-field create from the search string, no modal, no automatic DB insert. Use createOptionForm() / createOptionUsing() when the new record needs multiple fields or a server-side primary key.
Call disabled() before relationship() on multi relationship selects (Filament requirement).
HTML labels, wrap, placeholder, disabled options, affixes, boolean, position
Cascading options (dependsOn)
resetOnDependencyChange(). The next region list contains California and Texas.
dependsOn() refreshes dependent choices; it does not decide whether an existing selection should be preserved. Enable resetOnDependencyChange() on the child for an immediate browser reset, including a non-clearable field. It clears a single selection to null and a multiselect to []. It is off by default so dependencies that only change labels or ordering do not unexpectedly clear data.
Keep the parent’s explicit PHP reset callback for server-driven state updates and the server-side form lifecycle. For a dependent multiselect, use $set('region', []) instead of null. Server-side Select validation also rejects a submitted region that is not in the current country’s allowed options. A client reset is not a replacement for validation or authorization.
Use dependsOn(['country', 'service'], fn (?string $country, ?string $service): array => ...) for several dependencies; callback arguments follow the path order. Paths are relative to the field’s schema, including repeater items. Reset a child’s previous selection when it no longer belongs to the parent’s allowed choices.
If only particular fields need a refresh, use Filament’s partial-render configuration for those fields. Skip rendering only when no other visible content depends on the changed value.
Related components
Large catalogs and virtualization
- Local catalog
- Remote catalog
Use
options() when the catalog is already available locally. Virtualization reduces mounted rows; it does not avoid sending the catalog to the browser.$productOptions is an application-provided key/label map. The default threshold is 100 projected rows. Rows are measured as they appear, and the visible window follows the viewport. A grid uses measured grid-row geometry as well.optionsLimit(50) caps the display of a small nonvirtualized result set. Once virtualization is active, the full matching collection stays navigable; the mounted window controls the DOM budget.Menu dimensions
position('top') and position('bottom') request a particular side. Width and height settings are bounded; unsupported values produce a configuration error rather than an oversized panel. On mobile, the sheet owns its own responsive layout.
Search limits and cache
Publish/configureconfig/filament-flex-fields.php when you need different limits:
options() invalidates the field’s request cache. invalidateSelectSearchCache() is available when your application changes a provider’s result within the same request.
Page payloads must be JSON-compatible and remain within the configured size. Cursors must progress: repeated/cyclic cursors and malformed responses are rejected. On a failed load, already displayed options remain available and the menu provides retry feedback.
Icons and compact controls
null to an icon setter restores its configured default.
plainTrigger() when embedding a small select inside another control. selectionIndicator(false) removes the selected-row marker without changing the selected value.
Keyboard, mobile and RTL
Home and End inside a text input retain their normal caret behavior. Disabled rows cannot be selected. Multi-select checklists remain open while items are toggled; chips can be removed individually.
On narrow viewports, the menu becomes a sheet with its own search area. Open lists respond to window resizing and use touch-sized rows. The menu carries the field’s writing direction:
Saving selections to your database
Save a single selection to a suitable scalar column. The option key is saved, not its display label. A multiselect without a relationship can be saved in a JSON column with an array cast:relationship('tags', 'name')->multiple(), Filament saves the relationship; a JSON column is unnecessary. pivotData() can supply additional pivot attributes. Always authorize related records in your application.
Use the form’s validated state when saving. For a constrained multiselect:
Troubleshooting
An existing selection shows its key instead of its label
An existing selection shows its key instead of its label
For remote options, provide
getOptionLabelUsing() for a single value or getOptionLabelsUsing() for a multiselect. These resolvers must also be able to resolve saved records outside the current result page.A previously selected value fails validation
A previously selected value fails validation
Check whether the option is now disabled, no longer belongs to the dependent catalog, or is excluded by your relationship scope. Reset invalid child selections when their parent changes.
A new label is rejected
A new label is rejected
allowCreateOption() accepts a new text identity; createOptionForm() creates a record through a validated modal. Use the flow that matches your database column. Additional application rules still apply.Only some JSON builder settings are available
Only some JSON builder settings are available
The builder configuration table lists the supported declarative keys. Callbacks, relationships, pagination and dependencies belong in PHP. Do not assume every fluent method has a JSON key.