Using WordPress Media Library in Widgets options

So how to add WordPress media uploader popup to our custom widget?

That’s not so hard, hovewer there are a few caveats.

Let’s start from our simple widget class, I’d recommend to get example from https://codex.wordpress.org/Widgets_API#Example. Then we will need to add our new option, it will be called “image”. Just copy and paste fields in the “form” function, to render widget form and “update” function, to save this option.

I would recommend to catch php output buffer, it may be helpful if you will want to cache widget output. So our “widget” function will look like this:


	public function widget( $args, $instance ) {
		// Our variables from the widget settings
		$title = apply_filters( 'widget_title', empty( $instance['title'] ) ? __( 'Default title', 'text_domain' ) : $instance['title'] );
		$image = ! empty( $instance['image'] ) ? $instance['image'] : '';

		ob_start();
		echo $args['before_widget'];
		if ( ! empty( $instance['title'] ) ) {
			echo $args['before_title'] . $title . $args['after_title'];
		}
		?>

		<?php if($image): ?>
			<img src="<?php echo esc_url($image); ?>" alt="">
		<?php endif; ?>

		<?php
		echo $args['after_widget'];
		ob_end_flush();
	}

Our “form” function will be:


	public function form( $instance ) {
		$title = ! empty( $instance['title'] ) ? $instance['title'] : __( 'New title', 'text_domain' );
		$image = ! empty( $instance['image'] ) ? $instance['image'] : '';
		?>
		<p>
			<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
			<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>">
		</p>
		<p>
			<label for="<?php echo $this->get_field_id( 'image' ); ?>"><?php _e( 'Image:' ); ?></label>
			<input class="widefat" id="<?php echo $this->get_field_id( 'image' ); ?>" name="<?php echo $this->get_field_name( 'image' ); ?>" type="text" value="<?php echo esc_url( $image ); ?>" />
			<button class="upload_image_button button button-primary">Upload Image</button>
		</p>
		<?php
	}

And our update function:


	public function update( $new_instance, $old_instance ) {
		$instance = array();
		$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
		$instance['image'] = ( ! empty( $new_instance['image'] ) ) ? $new_instance['image'] : '';

		return $instance;
	}

As you can see, I’ve added a button underneath image input field with class “.upload_image_button”, but it will not work for now and to make it work we will need to add our custom admin javascript.

First what we need is to create our javascript file and put it into our theme, let’s create a file, called our_admin.js in assets/js under our theme folder.

Next we will need to register and enqueue our script to make it load in the admin panel, so lets add few lines to our widget class constructor function:


	function __construct() {

		// Add Widget scripts
		add_action('admin_enqueue_scripts', array($this, 'scripts'));

		parent::__construct(
			'our_widget', // Base ID
			__( 'Our Widget Title', 'text_domain' ), // Name
			array( 'description' => __( 'Our Widget with media files', 'text_domain' ), ) // Args
		);
	}

And we will need then “scripts” function in our widget class:


	public function scripts()
	{
		wp_enqueue_script( 'media-upload' );
		wp_enqueue_media();
		wp_enqueue_script('our_admin', get_template_directory_uri() . '/assets/js/our_admin.js', array('jquery'));
	}

As you can notice, we also added wp_enqueue_script( ‘media-upload’ ); and wp_enqueue_media(); to enqueue media library popup scripts.

And finally, here is our script, that will call WordPress media library popup and put selected image into the input field:


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

		});

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

So thats it, now, when you click on “Upload Image” button, WordPress media library popup will open and you will be able easily upload or select already uploaded image for widget options form.

42 thoughts on “Using WordPress Media Library in Widgets options”

  1. please can you set this all code in one file function.php
    it is difficult for me..

    1. Put above php codes in a class. You can put below codes in functions.php or create a new file and load it.

      add_action('widgets_init', 'my_widget');

      function my_widget() {
      register_widget('my_new_widget ');
      }

      // widget class
      class my_new_widget extends WP_Widget {
      // copy above php codes here
      }

  2. hy,
    thanks for the great resource!
    one thing i found out while implementing this is that it is better to change the click-handler (line 3 in your js) to
    $(document).live(“click”, “.upload_image_button”, function (e) {

    then it will also work after saving the widget, because the button will be a new one added with ajax and therefore the “on” – handler will not work anymore, while “live” works also on dynamic elements.

    matt

  3. Dude! You rock!!!!! Thanks so much for this, I must have looked at 34 other tutorials before landing on this one, and it’s just what I needed. The structure matches perfectly to my existing plugin, and it’s very clear and easy to follow. Awesome. My only suggestion would be to display a thumbnail of the image instead of just the filename. Thanks so much.

  4. Any ideas why this doesn’t work for me ?
    When I click upload image, the whole page reloads and nothing happens.
    Please let me know what may cause the problem. I can send you my code if this won’t show.

    See the Pen owNLwo by Karniej (@Karniej) on CodePen.

    1. You need to debug your app, check if button with class .upload_image_button is actually rendered, check if JS code was added to ad page, you can add console.log(‘Code added’) on top of your code, check your console to see this message. Check if there any error in console.

  5. Awesome tutorial. Saved me a ton of time as well!

    I’m trying to add multiple files and have set multiple to true and that is working, but when I try and get the selected files’ names on select, I’m struggling to get anything but the first. I tried looping through each selection with .each but I’m not sure what to call.

    // When an image is selected, run a callback.
    file_frame.on(‘select’, function () {
    var attachment = file_frame.state().get(‘selection’).each( function(){
    console.log($(this).attachment.url);
    //$(this).toJSON();
    });
    $button.siblings(‘input’).val(attachment.url);
    });

    This code logs an error “Cannot read property ‘url’ of undefined” – any ideas?

    Thanks!!!

    1. Hi, you probably need to specify variable in the each function:

      var attachment = file_frame.state().get(‘selection’).each( function( attachment ) {
      console.log( attachment.toJSON().url );
      });

      Otherwise try to cnonsole.log( file_frame.state().get(‘selection’) ); and see what kind of object is that, if it’s array you can use .forEach(function( attachment ) { … });

  6. Hey, great tutorial. I just have a question:
    when i’ve chosen a image and click on select, wordpress doesn’t notice that something changed in the URL textfield and the “Save” Button is still grey. I have to delete a letter or change something manually in on of the textfields to give wordpress the notification that i changed something.
    Did i miss something?

    1. Calling ‘change’ event after setting the value works for me

      So the line 26 will look like:
      $button.siblings(‘input’).val(attachment.url).change();

        1. Add `<img id="image_upload_preview" src="” alt=””>` above the image input field.

          To make the image preview watch for changes, add this line
          `$(‘#image_upload_preview’).attr(‘src’, attachment.url).change()`
          on the js file, below `$button.siblings(‘input’).val(attachment.url).change();` (around line 28 or 29).

          Hope it helps!

  7. So bad that we should first search in SO, then here, then in official documentation. Thank you! 3 years later still extremely useful!

    1. Check Sergei Varzin’s entry above. Adding .change() to line 26 of the .js, as suggested, worked for me.

      If the Upload button doesn’t do anything, it’s probably from the copy/paste of code, which your browser console should display an syntax error….usually ” and ‘ need retyped.

  8. Hello,
    thanks for your nice article. It’s working fine, except one thing. When you press the button to upload an image, the media selector is opened twice. Maybe i embedded the jQuery Script at the wrong place. I placed it behind the last parahraph in function form. Somebody know, what I have done wrong?

  9. Hy, thank’s for the idea
    After uploaded image, the “save” button don’t work for me!
    Thank’s for respond

  10. Why ATTACHMENT DISPLAY SETTINGS (image size selection and alignments) is not supported on media editor box. How to make this happen ?

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.