Latest Update: The plugin has been merged into WordPress core. So if you’re running WordPress 4.4 or newer, you automatically have this.
Update: The plugin created in this article has moved here and now uses the more appropriate srcset
attribute. It’s the official WordPress plugin of the Responsive Images Community Group and is endorsed by the WordPress Core Team. It sounds likely that this will make it into WordPress core eventually. Pretty cool!
The following post is guest co-authored by Tim Evko (@tevko). WordPress has a built-in media uploading system. When you upload an image, it automatically creates and saves different versions of it. Tim will show us how we can customize and exploit that ability to help us with responsive images in content.
If you’re like me, and you’re tasked with building responsive website that’s relatively easy to update, WordPress is most often the CMS you will be building that website on. If you’re more like me, you probably skipped over a responsive image solution in favor of making things easier for whomever will be doing the updating. Fortunately, with a few lines of PHP and some JavaScript, you can now add automatic responsive image functionality to your WordPress site.
Here I’ll show you how to add support for responsive images to your WordPress site in the form of a small WordPress plugin.
The Markup we ultimately want
We’ll be using the Picturefill library here. For now, we’ll use the markup that library suggests, which closely mimics what the <picture>
element will soon be.
<picture>
element is landing in browsers, so the plugin/code in this article has been updated to work directly with that syntax.<picture>
<source srcset="examples/images/extralarge.jpg" media="(min-width: 1000px)">
<source srcset="examples/images/large.jpg" media="(min-width: 800px)">
<source srcset="examples/images/medium.jpg">
<!-- fallback -->
<img srcset="examples/images/medium.jpg" alt="alt text">
</picture>
That is the basic markup pattern, although it can get slightly more complex to accommodate some browser bugs.
What we’re not going to do is use that markup directly in blog posts, we’re going to get WordPress to help us with that.
The Theme
All you need to do in your theme is make sure this one-liner is present in its functions.php file:
add_theme_support('post-thumbnails');
This will ensure that your theme gives WordPress permission to resize the uploaded images. Without it, the plugin won’t work.
The Plugin
This makes sense to do as a WordPress plugin, because we’ll want to keep it active no matter what theme is active. We can make it a folder with a PHP file in there to be the plugin code itself, and a copy of the Picturefill library:
Adding the library
Queue it up responsibility:
function get_picturefill() {
wp_enqueue_script('picturefill', plugins_url( '/js/picturefill.js', __FILE__ ));
}
add_action('init', 'get_picturefill');
That will ensure WordPress loads up this JavaScript library on the front end.
Define sizes
Tell WordPress what size images you want created upon uploading:
add_image_size('large-img', 1000, 702);
add_image_size('medium-img', 700, 372);
add_image_size('small-img', 300, 200);
You can set this up however you like. add_image_size has a variety of parameters you can adjust. In the case of a 1024×768 bunny rabbit JPG I uploaded, a bunch of versions get created:
Making a [shortcode]
Let’s extend this plugin, giving it some real functionality, by making a responsive images shortcode. That way we can put this right in the post content:
[responsive imageid="12" size1="0" size2="500" size3="1000"]
and it will output the markup we need for Picturefill.
We’ll split this up into three functions. One to define the shortcode and what HTML to output, one helper to return alt text, and one specifically to loop through and output the image sources.
function tevkori_get_img_alt( $image ) {
$img_alt = trim( strip_tags( get_post_meta( $image, '_wp_attachment_image_alt', true ) ) );
return $img_alt;
}
function tevkori_get_picture_srcs( $image, $mappings ) {
$arr = array();
foreach ( $mappings as $size => $type ) {
$image_src = wp_get_attachment_image_src( $image, $type );
$arr[] = '<source srcset="'. $image_src[0] . '" media="(min-width: '. $size .'px)">';
}
return implode( array_reverse ( $arr ) );
}
function tevkori_responsive_shortcode( $atts ) {
extract( shortcode_atts( array(
'imageid' => 1,
// You can add more sizes for your shortcodes here
'size1' => 0,
'size2' => 600,
'size3' => 1000,
), $atts ) );
$mappings = array(
$size1 => 'small-img',
$size2 => 'medium-img',
$size3 => 'large-img'
);
return
'<picture>
<!--[if IE 9]><video style="display: none;"><![endif]-->'
. tevkori_get_picture_srcs( $imageid, $mappings ) .
'<!--[if IE 9]></video><![endif]-->
<img srcset="' . wp_get_attachment_image_src( $imageid[0] ) . '" alt="' . tevkori_get_img_alt( $imageid ) . '">
<noscript>' . wp_get_attachment_image( $imageid, $mappings[0] ) . ' </noscript>
</picture>';
}
Then enabled the shortcode:
add_shortcode( 'responsive', 'tevkori_responsive_shortcode' );
Ideally you define your breakpoints in the plugin here and then don’t pass them at all when using the shortcode, like:
[responsive imageid="12"]
Then only use the shortcode attributes in the rare cases you need to override the breakpoints.
Altering the Media Uploader output
This shortcode will be tremendously useful (remember we can even adjust the markup to be the correct <picture>
in the future programmatically). But, how do we know the image ID to use? The Media Uploader UI doesn’t make that information available. It knows that ID though, and with a simple filter we can adjust what gets sent to the editor:
function responsive_insert_image($html, $id, $caption, $title, $align, $url) {
return "[responsive imageid='$id' size1='0' size2='600' size3='1000']";
}
add_filter('image_send_to_editor', 'responsive_insert_image', 10, 9);
Now selecting and inserting an image from the editor will work like this:
That’ll do it! Here you can see it working:
More you can do
- If you’re looking for a wider range of support, consider installing Matchmedia.js as well. It will make @media queries start working in older browsers, which then automatically makes Picturefill work as well.
- Rather than use shortcodes or adjust the media uploader output, you could make this work with a special image field via Advanced Custom Fields.
- You could adjust the markup to allow for align-* as WordPress likes to do. Or with
<figure>
and<figcaption>
elements. Also, alt tags. - You could have the settings for the breakpoints be settable via UI that the the plugin creates, rather than hard coded into the plugin.
- Here’s an alternate approach which uses Picturefill 2 and no shortcodes.
Here’s the GitHub repo if you want to contribute.
Wish there was something like this for Joomla!
Interesting. I’m curious how the user in the visual editor sees it?
I’ve always just solved the problem by making images in the content area set to max-width of 100% using media queries. That’s always handled 99% of the situations.
That’s not a bad practice. Making images fit their containers is great. But this is about serving different size images, which is about performance.
Yeah I thought about that. I can definitely think of a couple of situations where you’ve got to do something like this just due to the situation. Like everything else, it’s a great solution in the right applications.
I use the same idea Josh, but this is key!
This is pretty interesting, but the numbers would totally scare off clients, Joe’s, and the lazy. I think instead of defining breakpoints, which would probably always be the same anyways (and if not, you gotta spend time fudging the numbers perfectly), just set
article img{max-width: 100%}
and use the sizeX instead to load different images altogether.Some images are fugly when they shrink, like branding/pixelart/pics of my girlfriend, so being able to swap out a totally different renderings of that image I think would be a better use of time and bandwidth.
Hey Oz, fortunately this plugin has default options. If the user leaves something blank (other than the image ID) or selects an image from the wordpress uploader, the default sizes will take over and the necessary markup will still be generated. Hopefully that will take care of most of the laziness!
Wrote something really similar to this a while ago – the only difference is that in stead of using a shortcode, I am directly alter the output of any image (except for smilies) that is in the_content();. I can strongly advise to use it in conjunction with the Manual Image Crop plugin. This way you also have the possibility to crop every image size individually – thus enabling a slightly higher level of art direction (I say slightly higher because it still does not give you the possibility to use completely different images for each size).
Quick question: resizing the screen through orientation of the screen will keep loading the smaller and bigger picture constantly, is there a way to have it recognize the bigger picture was loaded already and resize that one with css instead?
This is get good result for responsive page But it is not responsive image, I think It take more time for page loading.
Not true. That’s the whole point. It only loads the one image appropriate for the media query. There is additional JavaScript, but the savings on one image likely mitigate that.
I like this alot, but all the typical attachment display settings no longer work, which my clients will want. Thanks for sharing this.
I use a similar, but more complex, system. Basically, I have an option in the media uploader that allows me to set the ‘context’ that the image will appear in, so it might be include, say: Sidebar Normal, Sidebar Fullwidth, Main Content Small, Main Content Normal, Main Content Fullwidth, and so on.
Each of these contexts then has an array of image sizes related to specific breakpoints, which allows me to have the appropriate image sizes for fairly complex layouts.
You do have to be a bit careful, though, or you end up with an awful lot of nodes in the markup and too many image sizes being created and stored.
I haven’t been using a shortcode as the output (didn’t think of it!), but as I use Jetpack subscriptions, that would mean there would be no images in the emails sent by jetpack, and the shortcodes would be shown instead. Not sure there’s a way around that.
Hope we will do same in jQuery! Great work!
This is awesome. I didn’t even think to do something like this. I usually stick with the insert large image and set max-width to 100% and height auto approach. I’ll try this on an upcoming client project and see how they like it.
Nit Pick: You want to enqueue the script on the ‘wp_enqueue_scripts’ action instead of the ‘init’ action. See http://wordpress.stackexchange.com/questions/55924/when-to-use-add-actioninit-vs-add-actionwp-enqueue-scripts
You’re right! Thanks for the catch! Mind Filing an issue on github?
Thanks for sharing this great solution. An error I ran into is that you have to use
<?php wp_head(); ?>
in your header.php and<?php wp_footer(); ?>
in your footer.php. When not using<?php wp_head(); ?>
, the images were not displayed.Jonas, those includes in your header and footer files are necessary, and should exist in your theme. Without them, you won’t be able to access a good deal of WordPress functionality
Tim, this does indeed look like a nice solution. Are you you going to submit it to the repository? I’m always a bit hesitant to try something like this on a live site with out it having gone through the review process.
Becky, I am considering possibly submitting this to the WordPress repository. Before I do that, I’d like to add some features and possibly a UI. If you’re looking to see how this plugin works, I would suggest installing it on a development / local / sandbox version of WP and test it out there
Sounds like a plan. I will play with it in a dev environment, but let us know if you publish it! Thanks
Thanks Tim, great post. I am however, looking for some CSS-Tricks for background images. If I have a background image for wrapper, I’ll use ‘background-size: cover and also something like center center in the background tag. What would be the correct way of doing this?
Thanks
Pretty interesting idea, never thought in this direction. The only downside is you can’t see the image in WYSIWYG, only a shortcode (and it’s a problem for content editors). And one more – pre-shortcoded images will be non-responsive (you need to manually edit your posts) and if you turn off the plugin – you’ll get no images at all. I’m going to try using default img tag and alter it with filter outputting the_content(). Looks like it should fix mentioned problems.
That’s a really interesting idea, filtering the image tag with the_content. And if you want a context, you can have an appropriate class or data- on the image tag that you can filter with. I’d be interested to see what you come up with.
Make sure to share when you do figure it out! :P
Thought about the content-filter for img a second before i saw your post. It should be possible with the use of ‘image_send_to_editor’ filter to place the needed variables like image-id in the img-string so it could be easier to find the image and get the id. From then on it is a piece of cake. ;-)
How would you go about doing this? I gave it a shot, but completely bombed in my effort.
This is great for fixed widths but as Draz commented, it seems a mechanism should be built to first check if a larger version has already been loaded and fallback to a max-width value rather than loading the next lighter version.
i.e. rotating a tablet acts as follows
landscape – js load 980px image version
portrait – js loads 480px image version (if landscape was already loaded, we’ve now request both images which is unnecessary)
edit fixed widths – I meant to refer when a device does not change its width after DOM load.
I think it will be better if script detect screen ratio and load matching ratio image with nearest matching size.
Still some resolutions and screen ratio will be ignored (manufacturers experimenting lot), so in that case script should look for image nearest size and apply max-width: 100% css to it.
You could also go for Noel Tock’s plugin Hammy
Hi, I have this working well, however in IE8-9, it doesn’t do anything?
I thought the picturefill library may provide a fallaback, on the Github page it says to do some shiv stuff to add the picture element, etc. however on for this version we don’t use that. Could it be that it just won’t work using this code?
What am I missing?
Thanks in advance! :)
I sometimes print images, I put the images on my computer screen with glue, if the image is too small I paint the surrounding com screen with black paint.
Its a slow process but whilst the paint is drying I get to take my dog out for a run.
A handy and cheap tip for letting your friend or friends (if you have more than one) see your images is to place them in an envelope and send them via the postal service, and its a little quicker than scraping the black paint from the screen, especially if you can’t find your dog,
I’m pretty new to wordpress and php.
How do I use this with Advanced Custom Fields?
Hi Lily,
There’s probably a better way to do it than this but this is how I used it with my advanced custom image field:
In my .php file
https://gist.github.com/funzeye/f48f7363ac9aeaa1e618
In this instance it is taken from my front-page.php file and I have the image inside of a figure – but that can be ignored.
Also remember to go into your ‘custom fields’ settings and change your images return value to Image ID if it is not already…
Take a look to the responsive websites examples – http://designmodo.com/responsive-design-examples/ very good practices.
I wonder if there is a way to create thumbnails at these sizes for existing images you already have on your site? Or will WordPress just do it on the fly if you try to use the image at a certain size? probably not, but would be nice!
Actually just found of couple of plugins that I think should do the job for me:
http://wordpress.org/plugins/ajax-thumbnail-rebuild/
http://wordpress.org/plugins/regenerate-thumbnails/
Is there some way the image could still be loaded in the editor but have the shortcode in the background? Clients won’t want to see this shortcode. Scary numbers! Where did my image go?!
Haven’t tried but in theory you would just need to remove the “Altering the Media Uploader” code from the function.php.
Then you would need to get the image id in your php page instead:
This snippet may help with that: https://gist.github.com/funzeye/f48f7363ac9aeaa1e618
Actually scratch that,
Im thinking you will probably just be using using “the_content” in your php, so the chances are you wont be able to just grab each image from the editor separately. So this probably won’t work.
Would also like to figure out a good solution for this…