Multiple Image Upload Metabox
I had so many requests about multiple image uploads in comments in this tutorial, so I decided to create a separate tutorial specifically about that.
Our goal is to create something like this:

Guys, there are actually two ways of creating a meta box like that, the first way is an easy-breezy way, but you have to install my Simple Fields plugin first, the second way – is to do everything manually from scratch. But please do not worry, no matter what method you choose, I am going to cover both of them in this tutorial.
Let’s begin with the simple one. So, in order to create a multiple image upload meta box, you just have to go through these two simple steps:
- Install and activate my Simple Fields plugin.
- Copy the following code snippet into your current theme
functions.php
file or to a custom plugin:
add_filter( 'simple_register_metaboxes', 'rudr_multiple_img_upload_metabox' );
function rudr_multiple_img_upload_metabox( $metaboxes ) {
$metaboxes[] = array(
'id' => 'my_metabox',
'name' => 'Meta Box',
'post_type' => array( 'page' ),
'fields' => array(
array(
'id' => 'my_field',
'label' => 'Images',
'type' => 'gallery'
),
)
);
return $metaboxes;
}
That’s really it, guys. One more thing – if you need to get images somewhere in your code, you can use get_post_meta()
function. Example:
$image_ids = get_post_meta( $post_id, 'my_field', true );
// Array( 4, 178, 778 )
Now let’s continue to the tough way.
The HTML part
<ul class="misha-gallery">
<?php
$image_ids = ( $image_ids = get_post_meta( $post_id, 'my_field', true ) ) ? $image_ids : array();
foreach( $image_ids as $i => &$id ) {
$url = wp_get_attachment_image_url( $id, array( 80, 80 ) );
if( $url ) {
?>
<li data-id="<?php echo $id ?>">
<span style="background-image:url('<?php echo $url ?>')"></span>
<a href="#" class="misha-gallery-remove">×</a>
</li>
<?php
} else {
unset( $image_ids[ $i ] );
}
}
?>
</ul>
<input type="hidden" name="my_field" value="<?php echo join( ',', $image_ids ) ?>" />
<a href="#" class="button misha-upload-button">Add Images</a>
- Let’s use the array of image IDs we can get using
get_post_meta()
function. Or you can useget_option()
,get_term_meta()
etc, depending on where you’re going to display this field. - Function
wp_get_attachment_image_url()
allows not only to get the image URL but it also returnsfalse
if there is no image with the given ID. - Do not forget to add some CSS to the
<span>
element, at leastdisplay:block
,width
andheight
.
The JavaScript part
The JavaScript code consists of three parts:
- “Add Images” click event.
- Click on remove specific image button “×”.
- Reordering images with drag and drop.
// click event
$( '.misha-upload-button' ).click( function( event ){ // button click
// prevent default link click event
event.preventDefault();
const button = $(this)
// we are going to use <input type="hidden"> to store image IDs, comma separated
const hiddenField = button.prev()
const hiddenFieldValue = hiddenField.val().split( ',' )
const customUploader = wp.media({
title: 'Insert images',
library: {
type: 'image'
},
button: {
text: 'Use these images'
},
multiple: true
}).on( 'select', function() {
// get selected images and rearrange the array
let selectedImages = customUploader.state().get( 'selection' ).map( item => {
item.toJSON();
return item;
} )
selectedImages.map( image => {
// add every selected image to the <ul> list
$( '.misha-gallery' ).append( '<li data-id="' + image.id + '"><span style="background-image:url(' + image.attributes.url + ')"></span><a href="#" class="misha-gallery-remove">×</a></li>' );
// and to hidden field
hiddenFieldValue.push( image.id )
} );
// refresh sortable
$( '.misha-gallery' ).sortable( 'refresh' );
// add the IDs to the hidden field value
hiddenField.val( hiddenFieldValue.join() );
}).open();
});
// remove image event
$( '.misha-gallery-remove' ).click( function( event ){
event.preventDefault();
const button = $(this)
const imageId = button.parent().data( 'id' )
const container = button.parent().parent()
const hiddenField = container.parent().next()
const hiddenFieldValue = hiddenField.val().split(",")
const i = hiddenFieldValue.indexOf( imageId )
button.parent().remove();
// remove certain array element
if( i != -1 ) {
hiddenFieldValue.splice(i, 1);
}
// add the IDs to the hidden field value
hiddenField.val( hiddenFieldValue.join() );
// refresh sortable
container.sortable( 'refresh' );
});
// reordering the images with drag and drop
$( '.misha-gallery' ).sortable({
items: 'li',
cursor: '-webkit-grabbing', // mouse cursor
scrollSensitivity: 40,
/*
You can set your custom CSS styles while this element is dragging
start:function(event,ui){
ui.item.css({'background-color':'grey'});
},
*/
stop: function( event, ui ){
ui.item.removeAttr( 'style' );
let sort = new Array() // array of image IDs
const container = $(this) // .misha-gallery
// each time after dragging we resort our array
container.find( 'li' ).each( function( index ){
sort.push( $(this).attr( 'data-id' ) );
});
// add the array value to the hidden input field
container.parent().next().val( sort.join() );
// console.log(sort);
}
});
Also don’t forget to enqueue scripts: jquery-ui-core
, jquery-ui-widget
, jquery-ui-sortable
and wp_enqueue_media()
.

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
Thanks a lot for this piece of knowledge. Really useful and straightforward. Cheers!
Nicely done!! Thanks for the post
Its works fine but remove event is not working properly. Thank you
I had to switch to something like this:
$('body').on('click', 'a.misha-gallery-remove', function(event) {...
to make the remove functionality work correctly.Can you provide full code please?
Also, did something like:
to remove the leading comma in the hidden field list.