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 :imagesis 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..