The Multi Select field type allows users to select multiple options from a searchable dropdown. It provides a modern interface with add/remove functionality, making it ideal for categorization, tagging, or any scenario requiring multiple selections from a predefined set.
array(
'type' => 'multi_select',
'id' => 'example_multi_select',
'label' => 'Tags',
'options' => array(
'php' => 'PHP',
'js' => 'JavaScript',
),
'min' => 1,
'max' => 5,
)For Default Field Properties, see Field Types Definition.
options(array|callable) — The available options for selection. Can be an associative array ('value' => 'Label'), an array of arrays withvalueandlabelkeys, or a callable that returns options dynamicallyoptions_key(string) — Optional: A key referencing a registered REST endpoint for fetching options asynchronouslyasync_params(array) — Optional: Additional parameters passed to the options callback. Supports dynamic value replacement using{{field_path}}placeholdersmin(integer) — Optional: Minimum number of itemsmax(integer) — Optional: Maximum number of itemsbuttons(array) — Optional: Custom button labels (add, remove, duplicate)disabled_buttons(array) — Optional: Buttons to disable (move, delete, duplicate)
The field stores an array of selected option values (strings) in the database:
array( 'php', 'js' )'product_tags' => array(
'type' => 'multi_select',
'id' => 'product_tags',
'label' => 'Product Tags',
'description' => 'Select tags that apply to this product',
'options' => array(
'new' => 'New',
'sale' => 'Sale',
'featured' => 'Featured',
'limited' => 'Limited Edition',
),
'default' => array( 'new' ),
),'color_choices' => array(
'type' => 'multi_select',
'id' => 'color_choices',
'label' => 'Colors',
'options' => array(
array( 'value' => 'red', 'label' => 'Red' ),
array( 'value' => 'green', 'label' => 'Green' ),
array( 'value' => 'blue', 'label' => 'Blue' ),
),
),'country' => array(
'type' => 'multi_select',
'id' => 'country',
'label' => 'Countries',
'options' => function ( array $args ): array {
return array(
'us' => 'United States',
'ca' => 'Canada',
'mx' => 'Mexico',
);
},
),The callback receives an array with the following keys:
value: The current value of the fieldsearch: The search term entered by the user- Additional parameters passed via
async_params
'product_type' => array(
'type' => 'select',
'id' => 'product_type',
'label' => 'Product Type',
'options' => array(
'physical' => 'Physical Product',
'digital' => 'Digital Product',
),
),
'product_features' => array(
'type' => 'multi_select',
'id' => 'product_features',
'label' => 'Product Features',
'options' => 'get_product_features',
'async_params' => array(
'type' => '{{product_type}}',
),
),The async_params support dynamic value replacement using {{field_path}} placeholders, following the same path syntax as Conditions:
- Dot notation for nested fields:
{{parent_field.nested_field}} #for relative references:{{#.sibling_field}}- Multiple
#for parent levels:{{##.parent_sibling_field}}
Defaults to true. When true, the async options list is fetched once and
shared across all fields using the same options_key/async_params (the
request does not include the field's value). Set to false to restore the
legacy behavior where each field sends its value in the options request —
needed only for callbacks whose async_params use dynamic {{field}}
placeholders or that otherwise depend on value when returning the browse list.
Regardless of cache_options, labels of saved values are resolved separately
from the browse list: server-side for storage-backed integrations (embedded in
the page as a per-options_key value→label map), or via a batched resolve
request in the block editor. In that call the callback receives value as an
array of the value(s) to resolve. Server-side, async_params entries containing
a {{field}} placeholder are dropped (their values are only known in the
browser), so the callback gets the static params plus value and must be able
to label a value from value alone. If it cannot, supply the labels yourself
via the wpifycf_preresolved_options filter:
add_filter(
'wpifycf_preresolved_options',
function ( array $preresolved, array $by_key ) {
foreach ( $by_key['my_terms'] ?? array() as $term_id ) {
$term = get_term( (int) $term_id );
if ( $term instanceof WP_Term ) {
$preresolved['my_terms'][ $term_id ] = $term->name;
}
}
return $preresolved;
},
10,
2
);$preresolved is the map of options_key => ( value => label ), $by_key the
map of options_key => saved values; a third argument carries the prepared
items.
$product_tags = get_post_meta( get_the_ID(), 'product_tags', true );
if ( ! empty( $product_tags ) && is_array( $product_tags ) ) {
$tag_labels = array(
'new' => 'New',
'sale' => 'Sale',
'featured' => 'Featured',
'limited' => 'Limited Edition',
);
echo '<div class="product-tags">';
foreach ( $product_tags as $tag ) {
if ( isset( $tag_labels[ $tag ] ) ) {
echo '<span class="product-tag product-tag--' . esc_attr( $tag ) . '">';
echo esc_html( $tag_labels[ $tag ] );
echo '</span>';
}
}
echo '</div>';
}'has_variations' => array(
'type' => 'toggle',
'id' => 'has_variations',
'label' => 'Product has variations?',
'title' => 'Enable product variations',
),
'variation_attributes' => array(
'type' => 'multi_select',
'id' => 'variation_attributes',
'label' => 'Variation Attributes',
'options' => array(
'color' => 'Color',
'size' => 'Size',
'material' => 'Material',
),
'conditions' => array(
array( 'field' => 'has_variations', 'value' => true ),
),
),$f = new \Wpify\CustomFields\FieldFactory();
$f->multi_select(
label: 'Tags',
options: array( 'php' => 'PHP', 'js' => 'JavaScript' ),
min: 1,
max: 5,
);- The Multi Select field is based on React Select, providing a modern searchable dropdown
- Selected options appear as chips/tags that can be individually removed
- The field prevents duplicate selections
- The stored value is always an array, even if only one option is selected
- Options can be provided as a static array, a callable, or via async REST endpoints
- Dynamic
async_paramsallow creating dependent select fields where available options update based on other field values