How to Use Select2 Multiselect in Meta Boxes

In this tutorial, I will show you two examples of how to use Select2 when creating custom fields in WordPress admin. Particularly, I will show you how to do it in meta boxes in the classic editor, but you can do the same for taxonomy settings or options pages.

In the first example, we will use Select2 to create a multiselect dropdown with tags. In the second one, it will become more interesting; we will do the same for posts, but also with an AJAX search (both examples will also be implemented in two ways – programmatically and with my Simple Fields plugin).

Before jumping into any of the examples below, you need to make sure that the Select2 library’s CSS and JS are added to your WordPress admin. For example, you can use a CDN version this way:

add_action( 'admin_enqueue_scripts', function(){

	wp_enqueue_style( 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css' );
	wp_enqueue_script( 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js', array( 'jquery' ) );

} );

Of course, you don’t need to do that if you decide to create the fields with my plugin.

Example 1. Select2 Multiselect Dropdown with Tags

As I already mentioned before, each example will be first implemented programmatically from scratch, and then we will do it with my Simple Fields plugin.

Below is the code of how you can do it from scratch:

<?php
// registeting our meta box
add_action( 'add_meta_boxes', function() {
	add_meta_box( 'misha_test', 'Meta Box for Select2', 'rudr_display_metabox', 'post' );
} );

// displaying the fields inside
function rudr_display_metabox( $post ) {

	if( $tags = get_terms( array( 'taxonomy' => 'post_tag', 'hide_empty' => false ) ) ) {

		$selected_tags = get_post_meta( $post->ID, 'some_tags', true );

		?>
			<p>
				<label for="some_tags">Tags</label><br />
				<select id="some_tags" name="some_tags[]" multiple style="width:99%">
					<?php
						foreach( $tags as $tag ) :
							$selected = is_array( $selected_tags ) && in_array( $tag->term_id, $selected_tags ) ? ' selected="selected"' : '';
							?><option value="<?php echo $tag->term_id ?>"<?php echo $selected ?>><?php echo $tag->name ?></option><?php
						endforeach;
					?>
				</select>
			</p>
		<?php
	}

}

// saving metabox data
add_action( 'save_post', function( $post_id ) {

	// autosave check ...

	// nonce check ...

	// post type check ...
	$tags = isset( $_POST[ 'some_tags' ] ) && is_array( $_POST[ 'some_tags' ] ) ? array_map( 'absint', $_POST[ 'some_tags' ] ) : array();
	update_post_meta( $post_id, 'some_tags', $tags );

} );

// initalizing select2
add_action( 'admin_footer-post.php', function() {

	?><script>
	jQuery( function($){
		$( '#some_tags' ).select2();
	} );
	</script><?php

} );

In the code above, please keep in mind the following:

And here is the result:

select2 WordPress example

Creating a meta box with a Select2 field with my plugin

In case you have the Simple Fields plugin installed on your website, you can create the same meta box with this simple code snippet:

add_filter( 'simple_register_metaboxes', function( $metaboxes ) {

	$tags = get_terms( array( 'taxonomy' => 'post_tag', 'hide_empty' => false ) );
	if( $tags ) {
		$metaboxes[] = array(
	 		'id' => 'misha_test',
	 		'name' => 'Meta Box for Select2',
			'post_type' => 'post',
	 		'fields' => array(
				array(
					'id' => 'some_tags',
					'label' => 'Tags',
					'type' => 'select',
					'placeholder' => 'Select cities…',
					'multiple' => true,
					'options' => wp_list_pluck( $tags, 'name', 'term_id' ),
				)
	 		)
	 	);
	}

	return $metaboxes;

} );

Looks much simpler, doesn’t it?

Plus, when you do it with the plugin, you don’t have to worry about enqueueing the Select2 library’s CSS and JS (the plugin will include the latest versions).

And as a result, we will have exactly the same meta box:

Select2 field in WordPress created with Simple Fields plugin
As you can see, the Select2 styles are slightly different – because my plugin uses the same styles WooCommerce does.

Another cool thing about my plugin is that when you change the hook from simple_register_metaboxes to simple_register_sidebars, the multiselect field is going to be displayed as a FormTokenField component in a Gutenberg sidebar:

Using Form Token Field component in Gutenberg plugin sidebars
Multiselect field in the Block Editor sidebar.

But the really cool thing about Select2 is that you can create not just a custom dropdown field, but a dropdown field with an AJAX search. And it is a real game-changer if you, for example, have hundreds of items to select from.

And right now, we’re going to do that just by adding one more field to our custom meta box.

By the way, if you don’t want to deal with all the code below, you can just scroll straight down to the plugin approach.

2.1. Adding the field

I think there is no point in copying the same code again, so you can just add the field to our previous code snippet, starting from line 26.

		<?php
	}
	$selected_posts = get_post_meta( $post->ID, 'some_posts', true );
	?>
		<p>
			<label for="some_posts">Posts</label><br />
			<select id="some_posts" name="some_posts[]" multiple style="width:99%">
				<?php
					if( $selected_posts ) :
						foreach( $selected_posts as $selected_post_id ) :
							$title = get_the_title( $selected_post_id );
							// if the post title is too long, truncate it and add "..." at the end
							$title = ( 50 < mb_strlen( $title ) ) ? mb_substr( $title, 0, 49 ) . '…' : $title;
							?><option value="<?php echo $selected_post_id ?>" selected="selected"><?php echo $title ?></option><?php
						endforeach;
					endif;
				?>
			</select>
		</p>

Nothing super-special here, just take a look at line 37 – we’re making the post title a little bit shorter if it is too long, and most likely won’t look that good in our select dropdown.

2.2. Select2 initialization

Why am I using jQuery? Because it is included in WordPress admin anyway.

$( '#some_posts' ).select2({
	ajax: {
		url: ajaxurl, // AJAX URL is predefined in WordPress admin
		dataType: 'json',
		delay: 250, // delay in ms while typing when to perform a AJAX search
		data: function( params ) {
			return {
				q: params.term, // search query
				action: 'mishagetposts' // AJAX action for admin-ajax.php
			}
		},
		processResults: function( data ) {
			var options = []
			if( data ) {
				// data is the array of arrays with an ID and a label of the option
				$.each( data, function( index, text ) {
					options.push( { id: text[0], text: text[1] } )
				})
			}
			return {
				results: options
			}
		},
		cache: true
	},
	minimumInputLength: 3 // the minimum of symbols to input before perform a search
});

2.3. AJAX search (PHP)

This is just a standard WordPress way of processing AJAX requests. We are using the action parameter from the previous code snippet as a part of the wp_ajax_ hook.

// wp_ajax_{action}
// no need for wp_ajax_nopriv_ because the meta box can only be used by WP users
add_action( 'wp_ajax_mishagetposts', 'rudr_get_posts_ajax_callback' );
function rudr_get_posts_ajax_callback(){
	// we will pass post IDs and titles to this array
	$results = array();
	query_posts( 
		array( 
			's'=> $_GET[ 'q' ], // the search query
			'post_status' => 'publish', // if you don't want drafts to be returned
			'posts_per_page' => 50 // how many to show at once
		)
	);
	if( $search_results->have_posts() ) :
		while( $search_results->have_posts() ) : $search_results->the_post();	
			// shorten the title a little
			$title = ( mb_strlen( $search_results->post->post_title ) > 50 ) ? mb_substr( $search_results->post->post_title, 0, 49 ) . '…' : $search_results->post->post_title;
			
			$results[] = array( 
				$search_results->post->ID, 
				$title,
			);
		endwhile;
	endif;
	echo json_encode( $return );
	die;
}

There is no real difference whether you’re going to use query_posts() or WP_Query or get_posts() to retrieve the posts in this code snippet. And there is no reason to reset the globals before the die; function anyway.

Here we go:

Select2 Multiple AJAX – WordPress example

Using Simple Fields to create a Select2 field with the AJAX search

Currently, my plugin supports two types of fields with the AJAX search:

And we already decided that we’re using the first one in our code. Let’s just add this line to our code above

array(
	'id' => 'some_posts',
	'label' => 'Posts',
	'type' => 'post',
	'multiple' => true,
	'post_type' => 'post',
	'placeholder' => 'Select a post',
),

And here is the final result:

Select2 multiple with search for WordPress
Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 15 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X