Oct 1, 2025

Responsive Images in Rails: Techniques and Tips

Dariusz Michalski

CEO

Learn how to implement responsive images in Rails to enhance website performance, improve user experience, and boost SEO.

Responsive images in Rails help your website load faster, look sharper, and improve user experience across all devices. They adjust image sizes and resolutions based on the user’s screen, reducing load times by up to 30% on mobile and improving Google rankings through better Core Web Vitals. Rails offers built-in tools like image_tag, picture_tag, and Active Storage to simplify implementation. You can also enhance performance with lazy loading, high-density images for Retina displays, and modern formats like WebP or AVIF.

Key Takeaways:

  • Use srcset and sizes with image_tag for responsive images.

  • Implement lazy loading (loading="lazy") to improve initial load times.

  • Optimise images with tools like image_optim, Cloudinary, or gems like Shrine.

  • Serve modern formats (WebP, AVIF) for smaller file sizes.

  • Test across devices to ensure correct image display and performance.

Rails makes it easy to integrate these techniques, ensuring your site is fast, efficient, and user-friendly. Let’s dive into how to put these strategies into action.

Make Your Site Lightning Fast With Responsive Images

How to Implement Responsive Images in Rails

Rails

Rails makes it straightforward to create responsive images that adapt to different devices and screen sizes.

Using image_tag Helper with srcset and sizes

Starting with Rails 5.2.1, the image_tag helper includes direct support for srcset and sizes attributes, making it easier to implement responsive images. The srcset attribute allows you to specify multiple image sources with varying sizes, while the sizes attribute tells the browser how large the image will appear at different viewport widths.

"The image_tag attribute has the srcset attribute which will enable you to load different images on different screens." - cryptosaurus_

For example, to set up width-based responsive images, you can define multiple image sizes and let the browser pick the best fit:

image_tag("pic.jpg", 
  srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], 
  sizes: "100vw"
)
# Generates: <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw">

The sizes attribute helps the browser decide how to render the image. For instance, (min-width: 650px) 50vw, 100vw means the image will take up 50% of the viewport width on larger screens and 100% on smaller ones.

For more intricate layouts, you can use a setup like this:

<%= image_tag(
    'register.png',
    alt: 'Rails Laundry Registration',
    title: 'Create New Registration',
    srcset: { 'register_480w.png'

This approach can cut mobile data usage by as much as 80%, making it a great choice for optimising performance on smaller devices.

Next, let’s explore how to handle high-density displays for sharper visuals.

Handling Retina and High-Density Images

High-density displays, like Retina screens, have a Device Pixel Ratio (DPR) of 2 or higher. These displays use multiple physical pixels to represent a single CSS pixel, which can make standard 1x images appear blurry.

To address this, you can use pixel density descriptors in Rails to serve higher-resolution images:

image_tag("icon.png", 
  srcset: { "icon_2x.png" => "2x", "icon_4x.png" => "4x" }
)
# Generates: <img src="/assets/icon.png" srcset="/assets/icon_2x.png 2x, /assets/icon_4x.png 4x">

While 2x images are essential for sharpness, anything beyond 2x usually offers diminishing returns in quality.

For dynamic image URLs, such as those generated by gems like Paperclip or CarrierWave, you can set up responsive images like this:

<%= image_tag(
    item.image.url(:thumb),
    alt: "#{item.title}",
    title: "#{item.title}",
    class: "th",
    srcset: { item.image.url(

When dealing with background images, CSS media queries are your best bet:

@media only screen and (-webkit-min-device-pixel-ratio: 2) and (min-width: 768px),
only screen and (min-resolution: 2dppx) {
    .image1 {
        background-image: url('image1@2x.jpg');
    }
}

Focus on using 2x images for elements that need to stand out, like logos, icons, or images containing text, to ensure they look crisp on all devices.

Once you’ve optimised for high-density displays, you can take it a step further by implementing lazy loading.

Adding Lazy Loading to Images in Rails

Lazy loading delays image loading until they are about to appear in the viewport, which can significantly improve initial page load times.

Rails 6.1 introduced support for the loading="lazy" attribute, making it easy to enable lazy loading:

<

This technique is particularly effective for pages with many images, ensuring a smoother user experience without compromising performance.

CSS and Media Query Methods for Responsive Images

Rails provides powerful HTML helpers for optimised image sources, but CSS takes it further by refining layouts and styles. Together, they create a seamless experience for responsive images across all devices.

Using CSS Frameworks for Responsive Layouts

CSS frameworks like Bootstrap and Tailwind CSS simplify responsive design, especially when integrated with Rails applications. They offer pre-built classes and utilities that make managing image layouts much easier.

Bootstrap's .img-fluid Class

Bootstrap’s .img-fluid class is a straightforward way to make images responsive. By applying max-width: 100%; and height: auto;, it ensures images scale within their parent containers:

<

Need something more complex? Bootstrap's grid system pairs perfectly with responsive images. For example, you can adjust column widths based on screen size:

<div class="row">
  <div class="col-sm-6 col-md-4">
    <%= image_tag "gallery-1.jpg", class: "img-fluid" %>
  </div>
  <div class="col-sm-6 col-md-4">
    <%= image_tag "gallery-2.jpg", class: "img-fluid" %>
  </div>
</div>

This setup makes images occupy half the width on smaller screens and one-third on medium screens or larger.

Tailwind CSS and Its Mobile-First Approach

Tailwind CSS takes a mobile-first approach, using responsive utility classes that adapt to different screen sizes. By prefixing classes with breakpoints, you can define sizes for each screen type:

<

Here, the image width adjusts to 4rem on mobile, 8rem on medium screens, and 12rem on large screens.

Breakpoint prefix

Minimum width

CSS equivalent

sm

40rem (640px)

@media (width >= 40rem) { ... }

md

48rem (768px)

@media (width >= 48rem) { ... }

lg

64rem (1024px)

@media (width >= 64rem) { ... }

xl

80rem (1280px)

@media (width >= 80rem) { ... }

2xl

96rem (1536px)

@media (width >= 96rem) { ... }

For Rails ViewComponents, ensure your tailwind.config.js includes the appropriate directories:

module.exports = {
  content: [
    './app/views/**/*.{erb,html}',
    './app/components/**/*.{erb,html}',
    './app/helpers/**/*.rb'
  ]
}

Tailwind's Container Queries

Tailwind also supports container queries, allowing you to style elements based on their parent container’s size. Add the @container class to the parent and use query variants like @md:flex-row:

<div class="@container">
  <%= image_tag "feature.jpg", 
      class: "w-full @md:w-1/2", 
      alt: "Feature image" %>
</div>

Writing Custom Media Queries for Image Styling

Frameworks are great, but custom media queries give you finer control for unique design needs.

Mobile-First Media Queries

Paul O’Brien, an advisor at SitePoint Forums, suggests focusing on design rather than device-specific sizes. Start with mobile styles and enhance for larger screens using min-width:

.hero-image {
  width: 100%;
  height: 200px;
  background-image: url('hero-mobile.jpg');
  background-size: cover;
}

@media screen and (min-width: 768px) {
  .hero-image {
    height: 400px;
    background-image: url('hero-desktop.jpg');
  }
}

CSS Variables for Maintainability

Centralising values with CSS custom properties makes media queries easier to manage:

:root {
  --image-width: 300px;
  --grid-gap: 1rem;
}

@media screen and (min-width: 768px) {
  :root {
    --image-width: 450px;
    --grid-gap: 2rem;
  }
}

.image-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--image-width), 1fr));
  gap: var(--grid-gap);
}

Handling High-Density Displays

For background images, resolution-based media queries ensure crisp visuals on high-density screens:

.logo {
  background-image: url('logo.png');
}

@media only screen and (-webkit-min-device-pixel-ratio: 2),
       only screen and (min-resolution: 2dppx) {
  .logo {
    background-image: url('logo@2x.png');
    background-size: 200px 100px;
  }
}

Orientation-Specific Styling

Adapt layouts to device orientation with these queries:

@media screen and (orientation: landscape) {
  .gallery-image {
    width: 50%;
    float: left;
  }
}

@media screen and (orientation: portrait) {
  .gallery-image {
    width: 100%;
    margin-bottom: 1rem;
  }
}

Selecting Measurement Units for Responsive Layouts

Choosing the right measurement units can further optimise your layouts.

Relative Units for Flexibility

  • Percentages: Useful for proportional scaling within containers.

  • Rem and em units: Provide consistent global and contextual sizing.

  • Viewport units (vw, vh): Scale directly with the screen size.

For example, percentage-based widths work well for images:

.responsive-image {
  max-width: 100%;
  height: auto;
}

While rem units ensure consistent spacing:

.image-container {
  padding: 1rem;
  margin-bottom: 2rem;
}

Advanced Functions: calc() and clamp()

The calc() function combines units for precise layouts:

.image-wrapper {
  width: calc(100vw - 2rem);
  max-width: calc(3 * var(--image-width) + 2 * var(--grid-gap));
}

Meanwhile, clamp() ensures fluid resizing within defined limits:

.hero-image {
  width: clamp(320px, 100%, 1200px);
  height: clamp(200px, 50vh, 600px);
}

Finally, include the viewport meta tag in your Rails layout for proper responsive behaviour:

<%= content_for :head do %>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
<

When using the <picture> element, apply responsive classes to the <img> tag inside it - not the <picture> tag itself - to ensure styles are inherited correctly.

Image Asset Optimisation in Rails

Building on responsive image techniques, optimising image assets further boosts Rails performance. This involves compressing images, selecting appropriate formats, and leveraging caching.

Image Compression Methods

Start by resizing images to their display dimensions before compression - this step alone can significantly reduce file size without affecting visual quality.

The image_optim gem is a handy tool for Rails applications. It acts as a Ruby interface for utilities like advpng, gifsicle, jpegoptim, optipng, and pngquant, enabling lossless compression while maintaining image quality.

# Gemfile
gem 'image_optim'
gem 'image_optim_pack' # Optional: includes binaries

If you're using CarrierWave, you can integrate the carrierwave-imageoptimizer gem and handle processing in background jobs to keep your app responsive:

class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::ImageOptimizer

  process :optimize
  process quality: 85
end

"This compresses all the images without any visual loss. The way this works is all the meta information about the image is stripped out. On average, this reduces the size around 20-30%."

  • SitePoint

Removing metadata like EXIF, IPTC, ICC, and XMP can reduce file sizes by 20–30% without compromising image quality.

For PNGs, tools like the piet gem can convert images to 8-bit while preserving transparency:

# Using piet gem
Piet.optimize('path/to/image.png')

JPEG optimisation focuses on creating progressive JPEGs and stripping metadata without re-encoding the core image data. Tools such as jpegoptim and jpegtran are effective for this:

# Progressive JPEG with 85% quality
jpegoptim --max=85 --strip-all image.jpg

For user-uploaded content, defer image processing to background jobs to avoid slowing down your app:

class ProcessImageJob < ApplicationJob
  def perform(image_id)
    image = Image.find(image_id)
    ImageOptim.new.optimize_image!(image.file.path)
  end
end

Once images are compressed, choosing the right format for each use case is the next step.

Selecting the Right Image Formats

Modern web development often requires serving different formats depending on browser capabilities. The <picture> element with <source> tags makes this possible:

<picture>
  <source srcset="<%= image_path('hero.avif') %>" type="image/avif">
  <source srcset="<%= image_path('hero.webp') %>" type="image/webp">
  <%= image_tag 'hero.jpg', alt: 'Hero image', class: 'img-fluid' %>
</picture>

Here’s a quick guide to selecting the best format:

Format

Best For

Compression

Browser Support

JPEG

Photos, complex graphics with gradients

Lossy, great for continuous tones

Universal

PNG

Graphics with solid colours, transparency

Lossless, but larger file sizes

Universal

WebP

General web images; smaller than JPEG

Lossy and lossless options

Modern browsers

AVIF

High-quality images, smaller than JPEG

Excellent compression, high quality

Limited but growing

WebP offers smaller file sizes with good quality and is favoured by Google, which may improve SEO. Always include fallbacks for browsers that lack support.

AVIF provides even smaller sizes and high quality, supporting features like HDR and transparency. However, it lacks progressive rendering, requiring the entire image to load before display.

For raster images, saving at 72 PPI is ideal for web use, balancing size and quality. Avoid excessive scaling to maintain clarity and prevent unnecessarily large files.

Vector graphics, like logos and illustrations, are best saved as SVG files. These scale without pixelation and usually result in smaller file sizes compared to raster images.

When handling uploads in Rails, validate the file formats to allow only web-safe options:

class Image < ApplicationRecord
  validates :file, presence: true
  validates :file, format: { 
    with: /\.(jpg|jpeg|png|gif|webp)\z/i,
    message: 'must be a valid image format'
  }
end

Caching and Asset Pipeline for Images

Rails’ asset pipeline ensures optimised image delivery. Rails 8 uses Propshaft as its default pipeline, which organises, versions, and serves static assets efficiently. This system uses fingerprinting, appending content-based digests to filenames for cache busting.

Store images in app/assets/images and use Rails helpers to reference them:

<%= image_tag "product/hero.jpg", alt: "Product showcase" %>
<

Before deployment, precompile assets to generate fingerprinted versions:

RAILS_ENV=production rails assets:precompile

This process creates files like hero-a1b2c3d4e5f6.jpg in public/assets, ensuring updated assets get served when content changes.

To improve caching, configure headers in the production environment:

# config/environments/production.rb
config.public_file_server.headers = {
  "Cache-Control" => "public, max-age=31536000"
}

For web servers like NGINX, add far-future expiry headers:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
  expires 1y;
  add_header Cache-Control "public, immutable";
}

To further enhance performance, integrate a CDN to serve assets from locations closer to users:

# config/environments/production.rb
config.asset_host = "https://cdn.example.com"

Enable compression on your CDN or server using Gzip or Brotli to reduce transfer sizes for all assets, including images.

Tools like Google Lighthouse can automate the optimisation process by identifying unoptimised images and providing actionable suggestions, such as resizing or converting formats.

Clear cached assets during development when necessary:

# Remove precompiled assets
rails assets:clobber

# Clear temporary files
rake tmp:clear

Modern CDNs often report cache hit ratios above 90% for static content, significantly cutting load times. Combined with HTTP/2 multiplexing, this setup can handle 30% more requests per second for static assets compared to HTTP/1.1.

Testing and Maintaining Responsive Images

Once you've implemented responsive images, it's crucial to test and maintain them regularly to ensure they perform well and provide a seamless user experience.

Testing Images Across Devices and Browsers

Testing responsive images on actual devices is essential. Tools like Chrome or Firefox DevTools in responsive mode allow you to check which image files are loaded and confirm that the correct srcset images are served. While testing, disable caching to get accurate results.

To cover a wide range of devices, focus on these commonly used screen sizes:

Device Type

Screen Size (px)

Typical Use Case

Small Mobile

320 × 568

Older iPhones, entry-level phones

Medium Mobile

360 × 800, 390 × 844

Most modern Android and iPhones

Large Mobile

412 × 915, 430 × 932

High-end or large-display smartphones

Tablet (Portrait)

768 × 1024, 800 × 1280

iPads, Android tablets (vertical)

Tablet (Landscape)

1024 × 768, 1280 × 800

iPads, Android tablets (horizontal)

Laptop/Desktop

1366 × 768, 1440 × 900

Standard laptops, lower-resolution displays

Large Desktop

1920 × 1080, 2560 × 1440

Full HD and QHD monitors

For a more extensive range of devices, platforms like BrowserStack and LambdaTest are invaluable. They let you test on real devices without the need for a physical lab. For instance, BrowserStack's Live platform offers access to over 3,500 browsers and devices, which is particularly useful given that mobile devices account for 53.42% of global web traffic.

If you're working on Rails applications, automated system tests can verify responsive behaviour. Here's an example:

class MobileSystemTestCase < ApplicationSystemTestCase
  def setup
    super
    current_window.resize_to(375, 812) # iPhone dimensions
  end

  def teardown
    current_window.resize_to(*ApplicationSystemTestCase::WINDOW_SIZE)
    super
  end
end

class ResponsiveImageTest < MobileSystemTestCase
  test "hero image loads correctly on mobile" do
    visit root_path

    hero_image = find('img[alt="Hero image"]')
    assert hero_image.present?

    # Verify the correct mobile image is loaded
    assert hero_image['src'].include?('hero-mobile')
  end
end

Also, test how images respond to orientation changes and viewport resizing. Once everything looks good across devices, keep monitoring and improving your strategy.

Regular Image Maintenance and Audits

Ongoing performance monitoring is key. Run monthly audits using tools like Lighthouse, and ensure every image has explicit dimensions to avoid layout shifts:

<

Automating image optimisation is another smart move. You can integrate this into your deployment pipeline with background jobs that compress uploads and convert them to modern formats like WebP or AVIF:

class OptimiseImageJob < ApplicationJob
  def perform(image_id)
    image = Image.find(image_id)

    # Generate WebP and AVIF versions
    create_webp_version(image)
    create_avif_version(image)

    # Compress original
    optimise_original(image)
  end
end

Set up alerts with tools like Sentry to catch and address any performance issues quickly.

Additionally, review your image inventory quarterly. Identify and remove unused assets to save on storage costs and reduce deployment sizes. Rails' asset pipeline makes it easy to track which images are actively used in your application.

Image Accessibility Requirements

Accessibility is just as important as performance. Use tools like WAVE, Axe, or Lighthouse to identify missing alt attributes, then manually review the text for accuracy:

# In your Rails system tests
test "all images have alt attributes" do
  visit page_path

  images_without_alt = page.all('img').select do |img|
    img['alt'].nil? || img['alt'].empty?
  end

  assert_empty images_without_alt, "Found images without alt text: #{images_without_alt.map { |img| img['src'] }}"
end

Every image should have an alt attribute that conveys its purpose. For decorative images, use alt="" to signal to screen readers that they can be ignored:

<!-- Informative image -->
<%= image_tag "sales-chart.png", alt: "Sales increased 40% from January to March 2024" %>

<!-- Decorative image -->
<

Ensure images with text or important information meet WCAG contrast standards. Tools like Colour Contrast Analyser can help you verify a contrast ratio of 4.5:1 for normal text and 3:1 for large text.

Also, test how responsive images behave when users zoom in up to 200%. Images should scale properly without causing horizontal scrolling or overlapping content. For instance:

.responsive-image {
  max-width: 100%;
  height: auto;
  object-fit: cover;
}

For interactive images like buttons or links, make sure they're fully usable with just a keyboard (Tab, Shift+Tab, Enter) and provide clear focus indicators.

With 57% of internet users saying they wouldn’t recommend a business with a poorly designed mobile site, maintaining accessible responsive images isn’t just good practice - it’s a direct factor in user satisfaction and business success. Regular testing, performance monitoring, and adherence to accessibility standards ensure your images enhance both usability and technical quality.

Conclusion

Responsive images play a crucial role in modern Rails applications, ensuring visual content looks great and loads efficiently across a wide range of devices. By using the srcset attribute and modern formats like WebP, developers can build faster, more efficient apps that not only enhance user satisfaction but also improve conversion rates.

Rails offers powerful tools to make this process straightforward. Features like the image_tag helper, combined with srcset and sizes attributes, the asset pipeline, and background job processing, provide a solid framework for serving optimised images. These tools don’t just improve performance - they also support better accessibility and search engine rankings.

Speaking of accessibility and SEO, the benefits go far beyond speed. Search engines favour mobile-friendly websites, and well-implemented alt attributes combined with responsive design ensure your site is usable for all visitors. As Frank Spillers, CEO of Experience Dynamics, puts it:

"Responsive is not an option – do it. And the reason is because that's where the world is at. Everyone expects things to be mobile-optimised, and responsive just means that if I switch from my laptop to my tablet to my phone, the site's going to fit to that resolution; it's going to kind of follow me."

These strategies also deliver long-term advantages. Efficient image delivery means reduced server load, lower bandwidth usage, and cost savings on hosting. Plus, using CDNs can significantly improve load times, especially for global audiences.

To implement these techniques effectively, it’s essential to test regularly on real devices, monitor performance consistently, and prioritise accessibility standards. By combining responsive images, lazy loading, and asset optimisation, you can create a seamless experience that benefits both users and your business.

At USEO, we specialise in helping businesses achieve these goals through advanced Rails development. Our expertise ensures your digital solutions are efficient, scalable, and optimised for all devices and user needs.

FAQs

How can I optimise responsive images in a Rails application for better performance and accessibility?

To make images in your Rails application responsive, take advantage of the srcset attribute. This allows you to deliver images optimised for different screen sizes and resolutions, helping your app load faster and providing a smoother experience for users. It's also a good idea to use modern image formats like WebP, which offer smaller file sizes without sacrificing quality. Don’t forget to compress your images to maintain a balance between visual appeal and performance.

Accessibility is another crucial factor. Always include meaningful alt text for your images - this not only improves usability for screen readers but also makes your application more inclusive. On top of that, implement caching strategies to reduce server load and enhance performance. By combining these practices, you can ensure your Rails application runs efficiently while keeping users happy.

What are the benefits of using modern image formats like WebP and AVIF in Rails, and how can I integrate them effectively?

Modern image formats like WebP and AVIF bring clear advantages to Rails projects. These formats achieve better compression and smaller file sizes compared to older formats like JPEG and PNG. The result? Faster loading pages and a smoother user experience. Among the two, AVIF stands out with its higher compression rates while still delivering impressive image quality. It even supports advanced features such as transparency and HDR.

To make the most of these formats in your Rails app, you can rely on image optimisation gems like image_optim or mini_magick. These tools allow you to convert and serve WebP or AVIF images dynamically, depending on what browsers can handle. This ensures your app runs efficiently while still playing nice with older browsers. By adopting these modern image formats, your application stays quick, efficient, and aligned with today’s web standards.

What is lazy loading, and how can it improve image performance in a Rails application?

Lazy loading is a method to boost website performance by postponing the loading of images until they’re actually visible in the user’s viewport. This not only shortens the initial page load time but also reduces bandwidth usage, offering a smoother browsing experience.

In Rails, you can enable lazy loading by simply adding the loading="lazy" attribute to your image tags in views. To maximise the benefits, pair lazy loading with responsive images and a content delivery network (CDN). This ensures your images are optimised for various screen sizes and resolutions, keeping your application fast and accessible - especially for users with slower internet connections or those browsing on mobile devices.

Related Blog Posts

Have a project idea? Let's talk and bring it to life

Your highly qualified specialists are here. Get in touch to see what we can do together.

Dariusz Michalski

Dariusz Michalski, CEO

dariusz@useo.pl

Konrad Pochodaj

Konrad Pochodaj, CGO

konrad@useo.pl

Have a project idea? Let's talk and bring it to life

Your highly qualified specialists are here. Get in touch to see what we can do together.

Dariusz Michalski

Dariusz Michalski, CEO

dariusz@useo.pl

Konrad Pochodaj

Konrad Pochodaj, CGO

konrad@useo.pl

Have a project idea? Let's talk and bring it to life

Your highly qualified specialists are here. Get in touch to see what we can do together.

Start a Project
our Office

ul. Ofiar Oświęcimskich 17

50-069 Wrocław, Poland

©2009 - 2025 Useo sp. z o.o.

Start a Project
our Office

ul. Ofiar Oświęcimskich 17

50-069 Wrocław, Poland

©2009 - 2025 Useo sp. z o.o.