Broken images are ugly.

This image is broken! Ugly, isn’t it?

But they don’t always have to be. We can use CSS to apply styles to the <img> element to provide a better experience than the default.

Two Facts About The <img> Element

To understand how we can style broken images, there are two facts about the way the <img> element behaves that we need to understand first.

  1. We can apply regular typography-related styling to the <img> element. These styles will be applied to the alternative text, if it is displayed, and will not affect the working image.

  2. The <img> element is a replaced element. This is an element “whose appearance and dimensions are defined by an external resource” (Sitepoint). Because the element is controlled by an external source, the :before and :after pseudo-elements typically shouldn’t work with it. However, when the image is broken and not loaded, these pseudo-elements can appear.

Because of these two facts, we are able to apply styles to the <img> element that will only appear when the image is broken, and will leave a working image unaffected.

Putting It Into Practice

Using this information, here are some examples of how we can style broken images, using the following broken link -

<img src="http://bitsofco.de/broken.jpg" alt="Kanye Laughing">  

Adding Helpful Information

One way we can handle broken images is to provide a message to the user saying that the image is broken. Using the attr() expression, we can even display the link to the broken image.

Using alt text to add helping information

img {  
  font-family: 'Helvetica';
  font-weight: 300;
  line-height: 2;  
  text-align: center;

  width: 100%;
  height: auto;
  display: block;
  position: relative;
}

img:before {  
  content: "We're sorry, the image below is broken :(";
  display: block;
  margin-bottom: 10px;
}

img:after {  
  content: "(url: " attr(src) ")";
  display: block;
  font-size: 12px;
}

Replacing The Default Alternative Text

Alternatively, we can use the pseudo-elements to replace the default alt text that shows, by positioning the pseudo-element on top of the default text, hiding it from view.

Using alt text to replace the default alt text

img { /* Same as first example */ }

img:after {  
  content: "\f1c5" " " attr(alt);

  font-size: 16px;
  font-family: FontAwesome;
  color: rgb(100, 100, 100);

  display: block;
  position: absolute;
  z-index: 2;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #fff;
}

Extra Styling

In addition to (or instead of) displaying a custom message, we can use the pseudo-elements to apply more styling to the broken image.

Using alt text to apply styling to the broken image

img {  
  /* Same as first example */
  min-height: 50px;
}

img:before {  
  content: " ";
  display: block;

  position: absolute;
  top: -10px;
  left: 0;
  height: calc(100% + 10px);
  width: 100%;
  background-color: rgb(230, 230, 230);
  border: 2px dotted rgb(200, 200, 200);
  border-radius: 5px;
}

img:after {  
  content: "\f127" " Broken Image of " attr(alt);
  display: block;
  font-size: 16px;
  font-style: normal;
  font-family: FontAwesome;
  color: rgb(100, 100, 100);

  position: absolute;
  top: 5px;
  left: 0;
  width: 100%;
  text-align: center;
}

 

If the image is not broken, with all the same styles applied to the element, the image is displayed normally. The pseudo-elements are not generated at all.

Image not broken

Browser Compatibility

Unfortunately, not all browsers handle broken images in the same way. For some browsers, even though the image is not displayed, the pseudo-elements don't show up at all.

Here’s what I’ve found from my tests -

Browser Alt Text :before :after
Chrome (Desktop and Android)
Firefox (Desktop and Android)
Opera (Desktop)
Opera Mini ✓ **
Safari (Desktop and iOS) ✓ *
iOS Webview (Chrome, Firefox, others) ✓ *

* The alt text will only show if the width of the image is large enough to accommodate it. If no width is specified for the img, the alt text may not be displayed at all
** Font styling not applied

For browsers that don’t support the pseudo-elements, the styles applied are ignored, so they don’t interfere in a breaking way. This means that we can still apply the styles and serve a more pleasant experience for users on a supporting browser.