WordPress development tutorials

This article details one method of creating a custom WordPress post type with the ability to upload and attach images using the built-in WordPress Media browser. This piece does not cover all the details and nuances of creating a custom post type, as this is covered well elsewhere.

In addition to highlighting the most important methods and code required, I have attached a fully functional demo plugin which you may feel free to download and use as a starting point or reference. Also note that the code within this example is borrowed and modified from my favorite ecommerce platform: WooCommerce.

The Goal

My goal tonight was to create a custom post type and add a meta box to display, and allow admin users to select and attach an image using the WordPress media box:

I want the selected image to be displayed in a custom meta box, similar to the Post Featured Image box, and I want it to be dead simple for you to get this working in your own project.

The Code

Skipping the boilerplate code required to register the custom post type (which I’m calling “book”), set up the custom meta box, etc (which you can review in the demo plugin), the first piece of code to examine is the image display/upload/remove links:

global $post;

$image_id  = get_post_meta( $post->ID, '_image_id', true );
$image_src = wp_get_attachment_url( $image_id );

  <img id="book_image" src="<?php echo $image_src ?>" style="max-width:100%;" />
  <input type="hidden" name="upload_image_id" id="upload_image_id" value="<?php echo $image_id; ?>" />
    <a title="<?php esc_attr_e( 'Set book image' ) ?>" href="#" id="set-book-image"><?php _e( 'Set book image' ) ?></a>
    <a title="<?php esc_attr_e( 'Remove book image' ) ?>" href="#" id="remove-book-image" style="<?php echo ( ! $image_id ? 'display:none;' : '' ); ?>"><?php _e( 'Remove book image' ) ?></a>

This snippet pulls the attached image (if any) from the post, displays it, and creates the “Set” (or upload) and “Remove” links. Also note the hidden input field which used to persist the attached image id to the post meta.

Next we hook up the upload link:

window.send_to_editor_default = window.send_to_editor;


  // replace the default send_to_editor handler function with our own
  window.send_to_editor = window.attach_image;
  tb_show('', 'media-upload.php?post_id=<?php echo $post->ID ?>&amp;type=image&amp;TB_iframe=true');
  return false;

The above javascript displays the WordPress media browser in the thickbox overlay when the “Set book image” link is clicked. With this and the previous piece of code the media browser will actually display and function as normal, until that you go to “Use this image” at which point the media box will go blank and not much else will happen. The reason for this is that when you choose an image from the media selector box it performs an asynchronous post to media-upload.php which returns a piece of Javascript similar to the following:

var win = window.dialogArguments || opener || parent || top;

win.send_to_editor('<a href=""><img src="" alt="" title="dracula" width="400" height="617" class="aligncenter size-full wp-image-1095" /></a>');

That call to win.send_to_editor() is sort of like invoking a callback or handler function, so we need to provide the function to do the image handling. That is the purpose of this line from above window.send_to_editor = window.attach_image;, so our next step is to provide that send_to_editor function, which in this example I’ve declared as attach_image():

window.attach_image = function(html) {

  $('body').append('<div id="temp_image">' + html + '</div>');

  var img = $('#temp_image').find('img');

  imgurl   = img.attr('src');
  imgclass = img.attr('class');
  imgid    = parseInt(imgclass.replace(/D/g, ''), 10);


  $('img#book_image').attr('src', imgurl);



  window.send_to_editor = window.send_to_editor_default;

This function creates a temporary img tag with the selected image, so as to more easily parse out the url and id, which are then used to display the selected image in our meta box, and set the image id to that hidden form field so we can save it permanently to the post meta. Finally the function performs a little clean up by closing the media browser, removing that temporary image tag, and restoring the original send_to_editor handler.

That covers the most important and difficult pieces of code, all that remains is hooking up the “Remove” link with something like:

$('#remove-book-image').click(function() {

  $('img').attr('src', '');

  return false;

And finally saving that image id to the post:

function book_meta_boxes_save( $post_id, $post ) {

  if ( empty( $post_id ) || empty( $post ) || empty( $_POST ) ) return;
  if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
  if ( is_int( wp_is_post_revision( $post ) ) ) return;
  if ( is_int( wp_is_post_autosave( $post ) ) ) return;
  if ( ! current_user_can( 'edit_post', $post_id ) ) return;
  if ( $post->post_type != 'book' ) return;

  update_post_meta( $post_id, '_image_id', $_POST['upload_image_id'] );
add_action( 'save_post', 'book_meta_boxes_save', 1, 2 );

Demo Plugin

By now hopefully you have a solid understanding of how to make use of the media browser to attach an image to a custom post type. If you’d like a fully functional example to play around with and extend for your own uses, feel free to download and make use of my demo plugin:

Custom Post Type Image Upload Demo

Published by Justin Stern

Justin is one of our co-founders, and is our resident overengineer. He likes to write developer tutorials and make black magic happen in our plugins. He thinks that writing code is a lot easier than writing words.


  1. Great tutorial thanks! I got it working on the backend perfectly, but now I don’t really know how to display the image on the front-end. Can you give me the code to do that please? I tried:

    ID, ‘image_src’, true); ?>


    ID, ‘image_id’, true); ?>

    and none of them worked.


    • This code doesn’t work. I can’t figure out why, but the image Id goes into the database as NaN which means it can’t be called for display in the loop.

  2. I think part of your example code got cut off, but once you get the image id, you should be able to use the wordpress image functions, like wp_get_attachment_url() for instance

    • would you have any ideas as to why when i run the script, the Image Id that is stored in $image_id goes in to the database as NaN. when I go to the database and change it manually to the correct id, then the image displays in my post. Otherwise, i jut get a blank image holder. You help would be appreciated as i found the code to be useful other than this which i can’t seem to solve.

  3. usage in theme template??



    thank you

  4. Hello, first of all, the tip has helped me a lot.

    Well, I wonder how can I put in categories plugin and how I visualize in my template, the book by category?

    thank you

    • hey claudio, I think that’s a bigger/more complicated question than I could really hope to answer in the blog comments

  5. Hi,
    can you please me, I want create custom post type plugin with my custom UI for front user. to add posts and edit posts, is there any tutorials on this please provide links.

    • Hey, here’s a great starting point: http://codex.wordpress.org/Post_Types Also a number of resources on that page, should have everything you need to get going if you’re comfortable with PHP

      • Hi,
        Thanks for your reply. I am a PHP developer and i created few small plugin using custom table. but this time i need to make WP custom post type plugin with my own UI like “CollabPress plugin” this plugin has project creation functionality from dashboard side and front end user can also create project.
        Finally i need to create post type plugin with own UI to submit post data from dashboard side and as well as fronted also. could please snuggest best reference tutorials for creation this plugin.


  6. There’s something wrong with your code. My image gets added to the database with value NaN. This means it cannot be called when you go back to change the image for the post. The problem is with the script:

    imgid = parseInt(imgclass.replace(/D/g, ”), 10);

    in the media uploader. The class cannot return an integer and therefore it returns the default NaN. I swapped this out with:

    imgid = ID ?>;

    which returns the id for the post, but not the id for the image. I still havn’t figured out a solution to the problem and any help would be much appreciated.

  7. As for Rodri’s problem, I think it will persist all the time the image ID is not being properly stored in the DB, but when it is then Justin’s suggestion will work.

  8. hey there thanks a lot for that great tutorial.
    when i test it local than works everything fine, but as soon i want to use your code live i get this error.

    Fatal error: Cannot redeclare class Custom_Post_Type_Image_Upload in
    …./wp-content/plugins/custom_post_image_upload.php on line 15

    can you maybe help me there?

  9. I’m trying to add an image upload to a second Custom Post Type. I got your plugin working perfectly for the first one, but can’t figure out how to do it for another one. Let me know what to do please.

    Thank you.

  10. Hey Justin Stern !
    please tell my. How to get image from this post type display in to front-end. I’m install plugin and activeted on back-end successfully, everything work fine in the back-end but i can’t out put display image in front-end.
    please help me.
    thank you !

  11. Hi,

    when I try to include this code in my plugin, i get the media box, but it doesn’t show the insert button.
    I am only capable of browsing through all media…
    I am using wordpress 3.8…also horizontal scrollbar is missing on the popup window..
    Any solution??


    • Not sure, it may well be broken with the new media browser recently introduced into WordPress, I really haven’t had a chance to go back and revisit this solution with it. Sorry about that!

  12. I’ve tested this straight up in WP 3.8 and it’s working fine.

    To answer some questions about how to use this in a loop – a simple integration is :


    if ( get_post_meta( get_the_ID(), '_image_id', true ) ) :
    $image = get_post_meta( get_the_ID(), '_image_id', true );
    echo '’ ;


  13. Was working fine, until I recently updated to the latest version of wordpress. Can’t seem to figure out why it isn’t working anymore 🙁

  14. Hi There. Is is possible to have more than just one image upload?


  15. how to delete image form galery..?

  16. For the problems with ID being NaN replace:

    imgid = parseInt(imgclass.replace(/D/g, ”), 10);
    imgid = parseInt(imgclass.replace(/\D/g, ”), 10);
    (notice the backslash)

    Fixed it for me. Actually it’s strange it ever worked: “\D” is just the short-form of [^0-9].
    The “,10)” (second parameter of parseInt) can also be omitted, it’s clear that we use the decimal system.

  17. Hello, I just wonder how to simply add a new field for an URL ? Is that possible without changing the image upload feature ?

Hmm, looks like this article is quite old! Its content may be outdated, so comments are now closed.