How to Upload Multiple Images using MediaUpload Component

While working on a website, you may want to display multiple images. It can be the image gallery, carousel, bulk photos, etc. To build this functionality, you need the option at the backend to upload multiple images. These images will be rendered at the front end. In this tutorial, we’ll build a custom Gutenberg block that will allow you to upload multiple images. The block will also have the option to delete these images.

Getting Started

Between static and dynamic blocks, I always preferred a dynamic block. The static block stores the markup in the database whereas the dynamic block stores only attributes in the database. These attributes can then be used to render the front end using PHP. For our custom block, the attribute will be an array of attachment ids. This array will be available on the front end and you can build anything out of it.

Head over to the wp-content/plugins directory in the terminal and run the below command.

npx @wordpress/create-block artisansweb-block --variant=dynamic

This command will create the artisansweb-block folder into the plugins directory. From the terminal, run npm start command from inside this artisansweb-block folder. This command should remain active while we developing a block.

Define Attributes for Custom Block

As we are dealing with multiple images, we should define the attribute with the type array. Add below lines in the src/block.json file.

{
    ...
    "attributes": {
   	 "images": {
   		 "type": "array",
   		 "default": []
   	 }
    }
}

Of course, at first, we don’t have any values so the default value is set to empty array [].

MediaUpload Component

MediaUpload is a React component used to open a WordPress media modal. From this modal, you can either upload new images or choose existing ones. This component provides the object of the selected images which helps to set the attributes value.

Before using this component, you first need to import it. Add the below statement to the src/edit.js file.

import { useBlockProps, MediaUpload, MediaUploadCheck } from '@wordpress/block-editor';

Let’s import a few more components required for different reasons.

import { Button, Spinner, BaseControl } from '@wordpress/components';
  • Button – I’ll add 2 buttons Add Images, and Remove Image using this component.
  • Spinner – The Spinner component helps to show a loader until the image object is fully loaded from the server.
  • BaseControl – It adds a label to your custom block.

Next, write a JSX code that allows you to upload multiple images into your block.

export default function Edit({ attributes, setAttributes }) {
    return (
   	 <div { ...useBlockProps() }>
   		 <BaseControl label="Artisans Web - Upload Multiple Images">
   			 <MediaUploadCheck>
   				 <MediaUpload
   					 onSelect={ ( media ) =>
   						 setAttributes( { images: media.map(image => image.id) } )
   					 }
   					 multiple={true}
   					 allowedTypes={ ['image'] }
   					 value={ attributes.images }
   					 render={ ( { open } ) => (
   						 <div>
   							 <Button variant="secondary" onClick={ open }>Add images</Button>
   						 </div>
   					 ) }
   				 />
   			 </MediaUploadCheck>
   		 </BaseControl>
   	 </div>
    );
}

Head over to the backend and add your block to the editor. You can now upload multiple images and these images will be stored in the database. You can check this entry in the post_content column of the wp_posts table.

However, the work is not done yet. The uploaded images should also be displayed in the editor. It can be done via useSelect hook. This hook helps to retrieve dynamic data from the server.

useSelect Hook

We have stored attachment ids in the database. Using useSelect hook, we’ll loop through each attachment, fetch its data from the server and build the image object. This object will help to display images in the editor. Let’s import this hook into the block.

import { useSelect } from '@wordpress/data';

The code with useSelect hook will be as follows.

export default function Edit({ attributes, setAttributes }) {
    const { mediaIds, medias } = useSelect( select => {
        const medias = [];
        attributes.images.forEach( (id) => {
            const img = select('core').getMedia(id);
            if ( img ) {
                medias.push(img)
            }
        });
        return {
            mediaIds: attributes.images,
            medias: medias
        }
    }, [attributes.images] )

    return (
    ...
    );
}

Once you are ready with your media objects, the next thing is to display images in the editor. To achieve this, we’ll modify the code written for render props into the MediaUpload.

<MediaUpload
	onSelect={ ( media ) =>
		setAttributes( { images: media.map(image => image.id) } )
	}
	multiple={true}
	allowedTypes={ ['image'] }
	value={ attributes.images }
	render={ ( { open } ) => (
		<div>
			{ mediaIds.length > 0 && medias.length == 0 && <Spinner /> }
			{ !! medias && medias &&
				medias.map( image => 
					<div className="image-container">
						<img src={ image.source_url } /> 
						<Button onClick={ () => setAttributes( { images: attributes.images.filter(function(item){return item !== image.id}) } ) } isLink isDestructive>
							Remove image
						</Button>
					</div>
				)
			}
			<Button variant="secondary" onClick={ open }>Add images</Button>
		</div>
	) }
/>

Your images should now display on the block. But, it’s not looking nice. It needs some styling.

I wrapped the img tag and Remove image button inside the div having a class image-container. Let’s add some styling using this container. The below code will go directly into the src/editor.scss file.

.wp-block-create-block-multiple-images {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    
    // Adjust the margin and width as per your requirement
    margin: 20px;
    
    .image-container {
     	position: relative;
     	margin: 10px;
   	    float: left;
      
     	img {
            display: block;
            width: 200px; // Adjust the image width as per your requirement
            height: auto;
     	}
      
        button {
            position: absolute;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
            margin-top: 10px;
        }
    }
}

Feel free to adjust this styling as per your convenience. After this, your block will look like as screenshot below.

multiple-images-in-block

Bravo! You’ve successfully completed the back-end work of your custom block. Let’s move to the front end.

Render Front End

When you add the images to our block, it stores the attachment ids in the database as follows.

<!-- wp:create-block/multiple-images {"images":[43,38,39,28]} /-->

These values are available to the $attributes variable in your src/render.php file. This file is responsible for printing your block content on the front end.

To print the images using the block attributes(images in our case), I’ll write the code as follows.

<div <?php echo get_block_wrapper_attributes(); ?>>
    <?php
	foreach( $attributes['images'] as $image ) :
    	echo wp_get_attachment_image($image, 'medium');
	endforeach;
	?>
</div>

Here, I am just displaying images with medium sizes using the wp_get_attachment_image() function. It renders an HTML img element for the given attachment id.

Now it’s up to you how to render these images on your website. You can wrap them inside the carousel, columns, etc. Take a note to add styling on the front end, you require to add CSS into the src/style.scss file.

That’s it! I hope you understand building a custom block to upload multiple images. If you want to add content along with these images then check out my article on using RichText and MediaUpload components.

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

Leave a Reply

Your email address will not be published. Required fields are marked *