Rails 7.1 adds picture_tag helper to support HTML5 picture element

railsJuly 25, 2023Dotby Alkesh Ghorpade

HTML5 introduced the picture tag, represented as <picture> which allows developers to provide multiple sources of an image and specify different versions of the image for different scenarios, such as different screen sizes or resolutions.

The <picture> HTML element contains zero or more <source> elements and one <img> element.

Each <source> element specifies a different image source using the srcset attribute, which contains the image's URL and, optionally the image's width descriptor. The browser selects the most appropriate image source based on the available options.

Here's an example of how the <picture> element is typically used:

  <source srcset="large-image.jpg" media="(min-width: 900px)">
  <source srcset="medium-image.jpg" media="(min-width: 600px)">
  <source srcset="small-image.jpg">
  <img src="fallback-image.jpg" alt="Description of the image">

The browser will use the following image sources, depending on the width of the current viewport:

  • large-image.jpg for viewport widths of 900 pixels or more
  • medium-image.jpg for viewport widths of 600 pixels or more but less than 900 pixels,
  • small-image.jpg for viewport widths of less than 600 pixels

If no sources are supported, or if there is an error loading the image, the fallback-image.jpg will be displayed instead.

The HTML <picture> is a more robust version of the <img>. Rails ActionView has an image_tag helper method that renders the <img>, but until Rails 7.1, the support for picture tag was missing.

Before Rails 7.1, there were gems like next_gen_images and picture_tag, that provided the picture_tag helper method.

With Rails 7.1, the picture_tag helper method is included in ActionView. picture_tag can be added to the view file like the image_tag.

<%= picture_tag("profile.webp") %>

The above command will generate the following HTML.

  <img src="/images/profile.webp" />

In this example, only one parameter was passed to the method, so the HTML generated includes only the <img> tag.

You can pass multiple <source> options to the method to define multiple sources for the image.

<%= picture_tag(
      class: "border-2",
      image: { alt: "Profile" }

The HTML output generated will include two <source> tags, one for profile.webp and another for profile.png. The browser will choose the profile.webp source if it supports the WebP format. If the browser does not support WebP, it will use the profile.png source instead.

<picture class="border-2">
  <source srcset="/images/profile.webp" />
  <source srcset="/images/profile.png" />
  <img alt="Profile" src="/images/profile.png" />

You can also pass a block to the picture_tag. While passing a block, you get complete control of the sources and the image tags. You can specify each source or image tag's size, width, or class.

<%= picture_tag(class: "border-2") do %>
  <%= tag(:source, srcset: image_path("profile.webp")) %>
  <%= tag(:source, srcset: image_path("profile.png")) %>
  <%= image_tag("profile.png", alt: "Profile") %>
<% end %>

The above code will generate the following:

<picture class="border-2">
  <source srcset="/images/profile.webp" />
  <source srcset="/images/profile.png" />
  <img alt="Profile" src="/images/profile.png" />

Using the media attribute, you can render the pictures based on the width of the screen size. You must set the media attribute when specifying the source.

<%= picture_tag do %>
  <%= tag(:source, srcset: image_path("ruby.webp"), media: "(min-width: 1500px)") %>
  <%= tag(:source, srcset: image_path("python.png"), media: "(min-width: 750px)") %>
  <%= image_tag("go.png", alt: "Language") %>
<% end %>

The above helper method will generate the HTML code as below.

  <source srcset="/images/ruby.png" media="(min-width: 1500px)" />
  <source srcset="/images/python.png" media="(min-width: 750px)" />
  <img alt="Language" src="/images/go.png">
Picture tag

To know more about the picture_tag feature, please refer to this PR.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right