Setup 🛠 Rails 8 App – Part 6: Attach images to Product model

To attach multiple images to a Product model in Rails 8, Active Storage provides the best way using has_many_attached. Below are the steps to set up multiple image attachments in a local development environment.


1️⃣ Install Active Storage (if not already installed)

We have already done this step if you are following this series. Else run the following command to generate the necessary database migrations:

rails active_storage:install
rails db:migrate

This will create two tables in your database:

  • active_storage_blobs → Stores metadata of uploaded files.
  • active_storage_attachments → Creates associations between models and uploaded files.

2️⃣ Update the Product Model

Configuring specific variants is done the same way as has_one_attached, by calling the variant method on the yielded attachable object:

add in app/models/product.rb:

class Product < ApplicationRecord
  has_many_attached :images do |attachable|
    attachable.variant :normal, resize_to_limit: [540, 720]
    attachable.variant :thumb, resize_to_limit: [100, 100]
  end
end

You just have to mention the above and rails will create everything for you!

Variants rely on ImageProcessing gem for the actual transformations of the file, so you must add gem "image_processing" to your Gemfile if you wish to use variants.

By default, images will be processed with ImageMagick using the MiniMagick gem, but you can also switch to the libvips processor operated by the ruby-vips gem.

Rails.application.config.active_storage.variant_processor
# => :mini_magick

Rails.application.config.active_storage.variant_processor = :vips
# => :vips

3️⃣ Configure Active Storage for Local Development

By default, Rails stores uploaded files in storage/ under your project directory.

Ensure your config/environments/development.rb has:

config.active_storage.service = :local

And check config/storage.yml to ensure you have:

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

This will store the uploaded files in storage/.


4️⃣ Add File Uploads in Controller

Modify app/controllers/products_controller.rb to allow multiple image uploads:

class ProductsController < ApplicationController
  def create
    @product = Product.new(product_params)

    if @product.save
      redirect_to @product, notice: "Product was successfully created."
    else
      render :new
    end
  end

  private

  def product_params
    params.require(:product).permit(:name, :description, images: [])
  end
end

Notice images: [] → This allows multiple images to be uploaded.


5️⃣ Update Form for Multiple Image Uploads

Modify app/views/products/_form.html.erb:

<%= form_with model: @product, local: true do |form| %>
  <%= form.label :name %>
  <%= form.text_field :name %>

  <%= form.label :description %>
  <%= form.text_area :description %>

  <%= form.label :images %>
  <%= form.file_field :images, multiple: true %>

  <%= form.submit "Create Product" %>
<% end %>

🔹 multiple: true → Allows selecting multiple files.


6️⃣ Display Images in View

Modify app/views/products/_product.html.erb:

<h1><%= product.name %></h1>
<p><%= product.description %></p>

<h3>Product Images:</h3>
<% product.images.each do |image| %>
  <%= image_tag image.variant(:thumb), alt: "Product Image" %>
<% end %>
<% product.images.each do |image| %>
  <%= image_tag image, alt: "Product Image" %>
<% end %>

Replacing vs Adding Attachments

By default in Rails, attaching files to a has_many_attached association will replace any existing attachments.

To keep existing attachments, you can use hidden form fields with the signed_id of each attached file:

<% @message.images.each do |image| %>
  <%= form.hidden_field :images, multiple: true, value: image.signed_id %>
<% end %>

<%= form.file_field :images, multiple: true %>

This has the advantage of making it possible to remove existing attachments selectively, e.g. by using JavaScript to remove individual hidden fields.


7️⃣ Get Image URLs

In Rails Console (rails c):

product = Product.last
product.images.each do |image|
  puts Rails.application.routes.url_helpers.rails_blob_url(image, host: "http://localhost:3000")
end

This generates a direct URL for each attached image.


8️⃣ Delete an Attached Image

To remove an image from a product:

product = Product.last
product.images.first.purge  # Deletes a single image

To remove all images:

product.images.purge_later


Final Thoughts

  • has_many_attached :images is the best approach for multiple image uploads.
  • Local storage (storage/) is great for development, but for production, use S3 or another cloud storage.
  • Variants allow resizing images before displaying them.

Check: https://guides.rubyonrails.org/active_storage_overview.html https://github.com/<username>/<project>/tree/main/app/views/products

Enjoy Rails! 🚀

to be continued..

Unknown's avatar

Author: Abhilash

Hi, I’m Abhilash! A seasoned web developer with 15 years of experience specializing in Ruby and Ruby on Rails. Since 2010, I’ve built scalable, robust web applications and worked with frameworks like Angular, Sinatra, Laravel, Node.js, Vue and React. Passionate about clean, maintainable code and continuous learning, I share insights, tutorials, and experiences here. Let’s explore the ever-evolving world of web development together!

One thought on “Setup 🛠 Rails 8 App – Part 6: Attach images to Product model”

Leave a reply to Shaffan Cancel reply