Crafting a Seamless Responsive Images Solution for WordPress

UPDATE: WordPress has released an official plugin to handle responsive images. I would disregard everything below and just use it.


Responsive images are the new hotness. They allow us to deliver an appropriately sized image to each user depending on a variety of conditions like screen size, viewport size, screen resolution, and more [*]. This allows us to give our users a better experience by getting them the images they want faster, while saving them bandwidth if they happen to be on their mobile device. We don’t want to be the reason they go over their monthly data package the nice man at the AT&T store helped them with.

So how do we do this in WordPress without sacrificing the usability of the WordPress visual editor?

I’ve recently started on a new WordPress project that will of course be fully responsive. One of the first real issues I ran in to was how to handle responsive images. It’s great that the new <picture> element is now a thing that we can comfortably use, but not only does the markup get to be quite a bit, it “breaks” WordPress’ WYSIWYG editor. We’re building on WordPress because of the ease of use for our users. Breaking something like image handling is not acceptable. Other solutions I’ve seen use shortcodes to handle the images. This creates a poor experience for our users because the visual editor now just shows a couple of brackets and some meaningless code. Not to mention they can’t go back and edit that image/code unless they know exactly what’s going on.

On setting out to create a solution, my requirements were:

  1. Don’t break the WYSIWYG. This means that not only should the images be able to be inserted from the media uploader, but once they’re in the page, the user should be able to use the quick edit button that shows up on hover.
  2. Use the default WordPress image sizes for simplicity. I think the default image sizes are pretty good, so let’s just stick with those for now.
  3. Keep the <picture> markup out of the editor. This is a pain that only the developer and the browser should have to see.
  4. Use the picturefill polyfill
  5. Use the <picture> markup in case we need more control in the future that <img srcset /> won’t allow.
  6. Have a <noscript> fallback just in case.
  7. Be absolutely transparent to the editors and users of our site. Everything should just work.

I’m not claiming that this is the correct solution or that this is a “perfect” solution. But after 6 iterations, this is what I’ll be moving forward with as it fulfills all of my concerns. If you’re wanting to use the code below, it can all go in your functions.php except the JavaScript at the end which goes in your main JS file.

The code is available on Github and an official plugin WordPress Plugin.

Step 1: Change the code injected by the WYSIWYG

We are going to need to hijack the Insert Media button so we can create our own image markup that we will parse later on. We’ll use the image_send_to_editor filter for this.

So what’s happening here?

First we grab the image attributes for the inserted image so we can get the width and height and set it to $imgAtts. Then we build out the actual image tag we are going to insert. The class, width, height, title, src and alt should be pretty familiar, but after that, we start to build out custom data attributes. All of these data attributes will be used by our parser in the coming steps.

The data-img-full, data-img-large, data-img-medium and data-img-thumbnail are set to the URLS for each of these respective images.

The data-size is the name of the image size, eg: full, large, etc. The data-id is the WordPress ID of the image itself.

Pretty simple so far.

Step 2: Use the_content filter to remove our dummy image and change it to the <noscript> markup

Now that our Post or Page contains everything we need to build out the <picture> markup, we are going to use the_content filter to re-write it and give the end user the correct code to render our responsive image.

All we’ve done now is find each image in our content and replace it with our new <noscript> markup that we will now parse with JavaScript on the frontend to build out the <picture> markup. Notice our <noscript> tag has the class ns-picturefill-img. We’ll use that to find any images that need to be converted to the new responsive images.

Step 3: Use JavaScript to find all our images that need to be made responsive

For the last step, we just need to create the actual <picture> markup from all the attributes that are now attached to the <noscript> tag. Everything below is pure JavaScript, but I am using the classList polyfill in my project, so you’ll want to add that if you’re going to use this as is.

While you’re adding the classList polyfill, make sure you’ve added the picturefill polyfill as well. Make sure you add them properly with WordPress‘ wp_enqueue_script.

Now, in your main JavaScript file for your site, we’ll need to use the following code.

I’m not going to go through all of this, hopefully the comments in the code are helpful enough. Essentially what we’re doing is finding all our images with the “ns-picturefill-img” class, creating a new <picture> element, adding all our <source> elements to it, appending the <img> tag and finally, appending the entire thing right after our <noscript> element.

The Final Output vs the WYSIWYG Input


What have we accomplished with all of this work?

We’ve created responsive images for every image inserted in the WordPress content area without breaking any WYSIWYG or basic WordPress functionality. We also have a fallback for people with JavaScript disabled or are on a device that doesn’t support it. Most importantly, everything we’ve done is completely transparent to the editors of our content.

None of this is being used in production quite yet, but the plan is to move forward with what I’ve detailed above. I’m certain there are bugs and many edge cases that I haven’t thought about. So if you find something, please comment as I’d love to make this better.

  • LucPestille

    This is almost exactly what I need (the RICG plugin is a bit too over-zealous with what it wants to do), but I’ve run into a problem; $dom->saveHTML(); gives back a full HTML page, with doctype/html/body tags – did you ever come across that, or fix it?

    (this is on top of not returning any markup where it should, but that’s a separate problem).