How to Use Select2 Multiselect in Meta Boxes

In this tutorial I am about to show you two examples how you can use Select2 when creating fields in WordPress admin. We are going to take a look at a meta boxes example specifically but you can easily use it for fields in taxonomies settings or options pages as well.

In the first example we will use Select2 to create a multiselect dropdown with tags, in the second one we will do the same for posts, but also with an AJAX search.

Before jumping into any of the examples below you need to make sure that Select2 library’s CSS and JS are added to your WordPress admin. I am going to use a CDN verson.

add_action( 'admin_enqueue_scripts', function(){

	wp_enqueue_style( 'select2', '' );
	wp_enqueue_script( 'select2', '', array( 'jquery' ) );

} );

Example 1. Select2 Multiselect Dropdown with Tags

In this example I am about two show you two ways of creating a multiselect field with Select2. First of all we will create a meta box from scratch and then I am going to do the same with my Simple Fields plugin.

Below is the code of creating it from scratch:

// 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 );

				<label for="some_tags">Tags</label><br />
				<select id="some_tags" name="some_tags[]" multiple style="width:99%">
						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


// 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() {

	jQuery( function($){
		$( '#some_tags' ).select2();
	} );

} );

In the code above please keep in mind the following:

And here is the result:

select2 WordPress example

Creating a meta box with Select2 field with my plugin

In case you have Simple Fields plugin installed on your website, you can create exactly 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(
					'id' => 'some_tags',
					'label' => 'Tags',
					'type' => 'select',
					'multiple' => true,
					'options' => wp_list_pluck( $tags, 'name', 'term_id' ),

	return $metaboxes;

} );

Looks much simple, doesn’t it?

Just don’t forget about enqueueing Select2 library’s CSS and JS (we did it in the beginning of this tutorial). And you will have exactly the same meta box:

select2 WordPress example
Meta box with “Classic Editor” plugin active.

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 real 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 into our custom meta box.

2.1. Adding the field

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

	$selected_posts = get_post_meta( $post->ID, 'some_posts', true );
			<label for="some_posts">Posts</label><br />
			<select id="some_posts" name="some_posts[]" multiple style="width:99%">
					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 ) . '&hellip;' : $title;
							?><option value="<?php echo $selected_post_id ?>" selected="selected"><?php echo $title ?></option><?php

Nothing super-special here, just take a look at line 37 – we’re making a 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 I am 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 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();
			'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 ) . '&hellip;' : $search_results->post->post_title;
			$results[] = array( 
	echo json_encode( $return );

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
Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 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