Select
The select lets the user pick from a list of options. Create one with Select::make() and pass the
options with ->options(). Each option is a label/value pair built with Select::option().
Select::make('country', 'Country') ->placeholder('Pick a country') ->options([ Select::option('Germany', 'de'), Select::option('France', 'fr'), Select::option('Spain', 'es'), Select::option('Italy', 'it'), ])- autoFocus:false
- columnWidth:"md"
- conditions:null
- creatable:false
- dependsOnAny:false
- dependsOnKeys:null
- disabled:false
- editablePrefill:false
- emptyLabel:"No options"
- helperText:null
- label:"Country"
- labelAction:null
- multiple:false
- name:"country"
- optionSchema:null
- options:[{"data":null,"label":"Germany","value":"de"},{"data":null,"label":"France","value":"fr"},{"data":null,"label":"Spain","value":"es"},{"data":null,"label":"Italy","value":"it"}]
- placeholder:"Pick a country"
- prefillRefreshOn:null
- prefillResetOn:null
- readOnly:false
- required:false
- searchPlaceholder:"Search…"
- searchable:false
- tabIndex:null
- tooltip:null
- value:null
Placeholder
Section titled “Placeholder”->placeholder() sets the text shown before a value is chosen. It is not selectable as a value.
->emptyLabel() sets the message shown in the dropdown when no options match (default “No options”),
and ->searchPlaceholder() sets the search box placeholder (default “Search…”).
Options from an enum
Section titled “Options from an enum”->enum() builds the options from a backed enum. Pass the enum class for every case, or an array of
cases for a subset. Labels come from the enum’s HasLabel contract when it implements one, otherwise
the case name is humanized.
Select::make('status', 'Status')->enum(OrderStatus::class);Multiple
Section titled “Multiple”->multiple() lets the user choose more than one option. The field submits an array of values.
Select::make('languages', 'Languages') ->multiple() ->placeholder('Choose languages') ->options([ Select::option('PHP', 'php'), Select::option('JavaScript', 'js'), Select::option('Go', 'go'), Select::option('Rust', 'rust'), ])- autoFocus:false
- columnWidth:"md"
- conditions:null
- creatable:false
- dependsOnAny:false
- dependsOnKeys:null
- disabled:false
- editablePrefill:false
- emptyLabel:"No options"
- helperText:null
- label:"Languages"
- labelAction:null
- multiple:true
- name:"languages"
- optionSchema:null
- options:[{"data":null,"label":"PHP","value":"php"},{"data":null,"label":"JavaScript","value":"js"},{"data":null,"label":"Go","value":"go"},{"data":null,"label":"Rust","value":"rust"}]
- placeholder:"Choose languages"
- prefillRefreshOn:null
- prefillResetOn:null
- readOnly:false
- required:false
- searchPlaceholder:"Search…"
- searchable:false
- tabIndex:null
- tooltip:null
- value:null
Searchable
Section titled “Searchable”A static ->options() select has no search box — the dropdown shows the full list. Call
->searchable() with a resolver to add a search box and resolve options on the server
(->searchPlaceholder() then sets its placeholder). There is no client-side filter for static
options — a list long enough to need filtering belongs behind a resolver.
->searchable() turns the select into a server-driven search. The resolver receives $search (the
query string) and returns the matching options; it may also inject $get/$state/$component and
any container type such as Request through closure evaluation. The
option value is the entity identifier and is fully controlled by the resolver, so it scales to large
datasets without shipping every option to the client.
Select::make('author_id', 'Author') ->searchable(fn ($search) => User::query() ->where('name', 'like', "%{$search}%") ->limit(10) ->get() ->map(fn (User $user) => Select::option($user->name, (string) $user->id)) ->all());On an edit form the stored value needs a label to display. ->resolveSelectedUsing() receives the
current value(s) — always as an array — and returns their options:
Select::make('author_id', 'Author') ->searchable(/* … */) ->resolveSelectedUsing(fn (array $values) => User::query() ->whereIn('id', $values) ->get() ->map(fn (User $user) => Select::option($user->name, (string) $user->id)) ->all());Options from a model
Section titled “Options from a model”Backing a select with an Eloquent model is the common case, so ->optionsFrom() takes an
OptionSource and wires both the search and the selected-label resolution from one declaration.
EloquentOptions is the shipped source:
use Lattice\EloquentOptions;
Select::make('author_id', 'Author') ->optionsFrom(EloquentOptions::make(User::class)->label('name'));->label() and ->value() pick the display and value columns (value defaults to the key). Add
->searchColumns(['name', 'email']) to match more than the label, ->scope(fn ($query) => $query->where('active', true))
to constrain the query, and ->limit(50) to cap results.
optionsFrom only resolves the options. The selected value (e.g. author_id) is a normal field
value, so it is validated and returned in the submitted array like any other — your handler persists
it. Lattice forms stay decoupled from the model.
Rich options
Section titled “Rich options”->optionSchema() renders each option through a schema of bound components instead of the plain
label — a second line, a badge, an avatar. Components bind option fields with
->dataKey($prop, $key), exactly like the stack column. The bindings
resolve against the option’s data record plus its label and value (label and value are
reserved and always resolve to the option’s own fields). The schema is declared once and ships once;
each option only carries its data, passed as the third argument to Select::option(). For static
options, the dropdown filter matches the option label; text rendered from data stays display-only.
Select::make('customer', 'Customer') ->placeholder('Pick a customer') ->options([ ]) ->optionSchema([ Stack::make()->schema([ Text::make('')->dataKey('text', 'label'), Text::make('')->dataKey('text', 'email')->size(Size::Sm)->color(Color::muted()), ]), Badge::make('')->dataKey('label', 'number'), ])- autoFocus:false
- columnWidth:"md"
- conditions:null
- creatable:false
- dependsOnAny:false
- dependsOnKeys:null
- disabled:false
- editablePrefill:false
- emptyLabel:"No options"
- helperText:null
- label:"Customer"
- labelAction:null
- multiple:false
- name:"customer"
- options:[{"data":{"email":"[email protected]","number":"K-10021"},"label":"Acme GmbH","value":"1"},{"data":{"email":"[email protected]","number":"K-10022"},"label":"Globex AG","value":"2"}]
- placeholder:"Pick a customer"
- prefillRefreshOn:null
- prefillResetOn:null
- readOnly:false
- required:false
- searchPlaceholder:"Search…"
- searchable:false
- tabIndex:null
- tooltip:null
- value:null
- align:null
- direction:null
- float:null
- gap:null
- height:null
- justify:null
- sticky:false
- width:null
- align:null
- color:null
- copyable:false
- dataBindings:{"text":"label"}
- size:"md"
- text:""
- align:null
- color:{"dark":null,"kind":"named","value":"muted"}
- copyable:false
- dataBindings:{"text":"email"}
- size:"sm"
- text:""
- color:null
- dataBindings:{"label":"number"}
- label:""
Searchable selects work the same way: a resolver returns options with data
(Select::option($user->name, (string) $user->id, ['email' => $user->email])), and
EloquentOptions attaches it with ->data() — pass the columns to include, or a closure receiving
the model for computed values:
Select::make('customer_id', 'Customer') ->optionsFrom( EloquentOptions::make(Customer::class) ->searchColumns(['name', 'email', 'customer_number']) ->data(['email', 'customer_number']), ) ->optionSchema([ Stack::make()->schema([ Text::make('')->dataKey('text', 'label'), Text::make('')->dataKey('text', 'email')->size(Size::Sm)->color(Color::muted()), ]), Badge::make('')->dataKey('label', 'customer_number'), ]);The selected value renders as the plain option label in the trigger and in multi-select chips.
Creatable / tags
Section titled “Creatable / tags”->creatable() turns the select into a tag input: typing a value that isn’t in the list and pressing
Enter (or a comma, or pasting a comma-separated list) adds it as a new option. Pair it with
->multiple() for a field that holds a set of tags.
Select::make('keywords', 'Keywords') ->multiple() ->creatable() ->placeholder('Add a keyword')- autoFocus:false
- columnWidth:"md"
- conditions:null
- creatable:true
- dependsOnAny:false
- dependsOnKeys:null
- disabled:false
- editablePrefill:false
- emptyLabel:"No options"
- helperText:null
- label:"Keywords"
- labelAction:null
- multiple:true
- name:"keywords"
- optionSchema:null
- options:[]
- placeholder:"Add a keyword"
- prefillRefreshOn:null
- prefillResetOn:null
- readOnly:false
- required:false
- searchPlaceholder:"Search…"
- searchable:false
- tabIndex:null
- tooltip:null
- value:null
The typed label becomes the value — free-text tags submit as a plain array of strings, and the
consumer creates matching records in handle().
Pair ->creatable() with ->optionsFrom() to back the tags with real records instead of plain
strings, and give an option a color in its data to color its chip:
Select::make('tags', 'Tags') ->multiple() ->optionsFrom(EloquentOptions::make(Tag::class)->label('name')->data(['color'])) ->creatable();A typed tag that matches no existing option still submits as its raw label, so the submitted array
mixes real ids (picked options) with new labels (typed tags) — the consumer resolves the array in
handle(), keeping the ids and creating records for the remaining labels.
->itemRules() validates each tag instead of the field as a whole:
Select::make('keywords', 'Keywords') ->multiple() ->creatable() ->itemRules(['string', 'max:40']);Common options
Section titled “Common options”Select shares label, default value, required, disabled, read-only, and visibility options with
every field — see Fields. For validation and conditional behavior, see
Validation and Conditional fields.