Upload images from custom plugin using the media modal

Ah!

The classic issue anyone that’s cared about their users’ experience has come to face.

As per my experience, wp.media is the way to go.

This is not my code, but it gets the job done. I’ve used it plenty.

I’ll explain what it does, piece by piece:

// Source: https://vedmant.com/using-wordpress-media-library-in-widgets-options/
jQuery(document).ready(function ($) {

   $(document).on("click", ".upload_image_button", function (e) {
      e.preventDefault();
      var $button = $(this);


      // Create the media frame.
      var file_frame = wp.media.frames.file_frame = wp.media({
         title: 'Select or upload image',
         library: { // remove these to show all
            type: 'image' // specific mime
         },
         button: {
            text: 'Select'
         },
         multiple: false  // Set to true to allow multiple files to be selected
      });

      // When an image is selected, run a callback.
      file_frame.on('select', function () {
         // We set multiple to false so only get one image from the uploader

         var attachment = file_frame.state().get('selection').first().toJSON();

         $button.siblings('input').val(attachment.url).change();

      });

      // Finally, open the modal
      file_frame.open();
   });
});

First thing’s first. We need a button, tied to an input, more about this later on. This can be in the customizer, or it can be anywhere you need it to be:

<button class="upload_image_button">Upload Image</button>

Then we need an ingester of data that is tied to this button. Unfortunately, this is out-of-scope, but assuming you’re doing this in a widget, this will to the trick:

<p>
    <label for="<?php echo $this->get_field_id( 'image' ); ?>">Image:</label>
    <br>
    <input class="widefat" id="<?php echo $this->get_field_id( 'image' ); ?>" name="<?php echo $this->get_field_name( 'image' ); ?>" value="<?php echo $instance['image'];?>" />
    <br>
    <button class="upload_image_button">Upload Image</button>
</p>

So far, we’re just targeting the button, going on:

  // Create the media frame.
  var file_frame = wp.media.frames.file_frame = wp.media({
     title: 'Select or upload image',
     library: { // remove these to show all
        type: 'image' // specific mime
     },
     button: {
        text: 'Select'
     },
     multiple: false  // Set to true to allow multiple files to be selected
  });

The code is well commented, but here’s the correspondent documentation files: https://codex.wordpress.org/Javascript_Reference/wp.media

So we’re just calling the WP JS API to access the media-frame. Now, on to getting the file path we need:

file_frame.on('select', function () {

Whenever we select something from the frame, get the path of the said file in an AJAX call, JSON format and let me use it –

var attachment = file_frame.state().get('selection').first().toJSON();

Remember that button that had to be connected to an input? Yea, so we insert this link we just got into that input:

$button.siblings('input').val(attachment.url).change();

And thus, a new link to your image is born.