Skip to main content
March 23, 2026Noble Desktop Publishing Team/10 min read

Product Images with Active Storage

Master Rails Image Management with Active Storage

Key Technologies in This Tutorial

Active Storage

Rails' built-in framework for handling file attachments with cloud storage optimization and local disk support.

ImageMagick

Cross-platform image editing tool that enables automatic resizing, cropping, and image transformation capabilities.

Arbre

Active Admin's object-oriented HTML markup language for building customizable admin interface forms.

Topics Covered in This Ruby on Rails Tutorial:

Configuring Active Storage, Configuring Image Processing, Modifying the Form, Customizing Images

Exercise Overview

In this comprehensive exercise, we'll transform your Rails admin interface by implementing dynamic image handling for each product. You'll learn to configure Active Storage—Rails' powerful file attachment framework—and integrate it seamlessly with your admin forms to replace static placeholder images with customizable, properly-sized product images.

  1. If you completed the previous exercise, you can skip the following sidebar. We strongly recommend completing the previous exercise (8A) before starting this one, as it establishes the foundational admin interface we'll be enhancing. If you haven't finished it, follow the setup instructions below.

    Tutorial Progress Flow

    1

    Setup Active Storage

    Configure Rails framework and install required dependencies for image processing

    2

    Modify Admin Forms

    Customize Active Admin interface to handle file uploads with proper validation

    3

    Implement Image Display

    Replace static images with dynamic product-specific images across all views

If You Did Not Do the Previous Exercise (8A)

  1. Close any files you may have open.
  2. Open the Finder and navigate to Class Files > yourname-Rails Class
  3. Open Terminal.
  4. Type cd and a single space (do NOT press Return yet).
  5. Drag the yourname-Rails Class folder from the Finder to the Terminal window and press ENTER.
  6. Run rm -rf nutty to delete your copy of the nutty site.
  7. Run Git clone https://bitbucket.org/Noble Desktop/nutty.Git to copy the That Nutty Guy Git repository.
  8. Type cd nutty to enter the new directory.
  9. Type Git checkout 8A to bring the site up to the end of the previous exercise.
  10. Run bundle to install any necessary gems.
  11. Run yarn install—check-files to install JavaScript dependencies.

Environment Setup Requirements

0/4

Introducing Active Storage

Now we'll tackle the challenge of replacing those repetitive Tinfoil Hat images with unique, properly-managed product images using Rails' sophisticated file handling system.

  1. Rails provides enterprise-grade file attachment capabilities through Active Storage, a framework that has revolutionized how Rails applications handle media files since its introduction in Rails 5.2. Active Storage excels at cloud storage integration—seamlessly working with Amazon S3, Google Cloud Storage, and Microsoft Azure—while maintaining excellent performance with local disk storage for development environments. In this exercise, we'll start with local storage and explore cloud deployment options when we deploy to production environments later in the course.

  2. One of Active Storage's most powerful features is automatic image processing and variant generation. This means you can dynamically create thumbnails, resize images, and apply transformations without manual intervention. To unlock this functionality, we'll install ImageMagick—the industry-standard image manipulation library—and integrate the image_processing gem into our application.

  3. ImageMagick is a battle-tested, cross-platform image processing powerhouse that handles everything from simple resizing to complex transformations like cropping, rotation, format conversion, and color adjustment. Install it via Homebrew with this Terminal command:

    brew install imagemagick
  4. Next, we'll add the Ruby gem that interfaces with ImageMagick. Open your Gemfile, scroll to the bottom, and add this dependency:

    # Resize image attachments
    gem 'image_processing'
  5. Save the file to ensure your changes are preserved.

  6. Return to Terminal and run the bundle command to install the new gem:

    bundle

    Remember: whenever you add new gems to your application, the Rails server must be restarted to load the new dependencies.

  7. Switch to the ruby tab in your Terminal window (where the server is running).

  8. Stop the server with CTRL–C.

  9. Restart the server to load your new gem:

    rails s
  10. Now we'll initialize Active Storage's database infrastructure. In a separate Terminal window, execute these commands:

    rails active_storage:install
    rails db:migrate

    These commands create the specialized database tables that Active Storage requires for tracking file attachments, their metadata, and associated variants.

  11. Let's examine Active Storage's configuration by opening config > storage.yml. This file contains environment-specific storage settings:

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

    Notice that the default service for local development is "Disk"—exactly what we need for this exercise. Files uploaded through your application will be stored in the "storage" directory within your Rails project root. The configuration file also includes sample setups for production-grade services like Amazon S3, Google Cloud Storage, and Microsoft Azure, which you'll likely use in production deployments.

  12. Close the configuration file—the defaults work perfectly for our current needs.

  13. With Active Storage configured, we need to update our Product model to recognize its image attachment capability.

  14. Open nutty > app > models > product.rb in your code editor.

  15. Add the Active Storage association at the bottom of the model class:

    class Product < ActiveRecord::Base
       validates :title, :sku, :price, presence: true
       validates :price, numericality: true
    
       has_one_attached :image
    end

    This single line of code establishes a powerful connection between your Product model and Active Storage, enabling each product to have an associated image with full processing capabilities.

  16. Save the file to commit your model changes.

Active Storage Architecture

Active Storage is optimized for cloud storage systems like Amazon S3 and Microsoft Azure, but works equally well with local disk storage for development environments.

Active Storage Installation Process

1

Install ImageMagick

Run 'brew install imagemagick' to add image processing capabilities to your system

2

Add image_processing Gem

Include the gem in Gemfile and run bundle to enable image transformation features

3

Initialize Active Storage

Execute 'rails active_storage:install' and migrate database to create required tables

Modifying the Form

With our backend configured, we'll now enhance the Active Admin interface to support file uploads, creating an intuitive admin experience for managing product images.

  1. Navigate to the admin interface in your browser: localhost:3000/admin

  2. If prompted, sign in with the credentials you created in the previous exercise. If you encounter login issues, use this troubleshooting solution:

    has_one_attached :image
    This single line of code in the Product model establishes the Active Storage association, enabling file attachment capabilities for product images.
    Default Admin Behavior

    When Active Admin files are blank, default behavior is assumed. To customize forms, you must explicitly define the form structure using Arbre syntax.

Creating a Login

  1. Open a Rails console by typing: rails c
  2. Execute this command to create a fresh admin user:
AdminUser.destroy_all
  AdminUser.create(email: 'admin@example.com', password: 'password')
  1. Type exit to leave the Rails console.
  2. You can now log in with email admin@example.com and password password.
  • Click the Products link in the navigation.

  • Select Edit next to any product to access the form we'll be customizing.

  • Open nutty > app > admin > products.rb in your code editor.

    Active Admin operates on a convention-over-configuration principle: when admin files are minimal, it generates default behavior automatically. Since products.rb is essentially empty, Active Admin creates a basic CRUD interface. To add our custom file upload functionality, we need to explicitly define the form structure.

    Security Parameters Required

    permit_params must be configured to specify which form fields are allowed for submission. This prevents unauthorized users from modifying restricted fields.

  • Arbre

    Active Admin uses Arbre, an elegant domain-specific language for building HTML interfaces. Arbre provides object-oriented HTML generation that's both intuitive and powerful, allowing you to create sophisticated admin interfaces with minimal code.

    For comprehensive documentation, visit: GitHub.com/gregbell/arbre

  • Below the commented code block, add this form structure:

    # end
    
       form do |f|
       end
    
    end
  • Within the form block, create input fields for all product attributes:

    form do |f|
       f.inputs "Product Details" do
          f.input :title
          f.input :sku
          f.input :price
          f.input :description
          f.input :specs
       end
       f.actions
    end
  • Let's improve the user experience by making the SKU label more professional and clear:

    f.input :sku, label: "SKU"
  • Save the file and reload the Edit Product page to see your improved form.

  • We can enhance usability further by adding contextual help text. Update the SKU field:

    f.input :sku, label: "SKU", hint: "A unique SKU for this product. Very important!"
  • Save and reload to see the helpful hint text appear below the field.

  • You may notice that the price field displays increment/decrement arrows in some browsers. For decimal currency values, a standard text input provides better user experience. Modify the price field:

    f.input :price, as: :string

    The as: parameter allows you to override Active Admin's default field type selection, giving you precise control over form behavior.

  • Save the file and reload to confirm the price field now behaves as a standard text input.

  • Return to your code editor to add the image upload functionality.

  • Form Customization Features

    Custom Labels

    Override default field labels using the label parameter to display more user-friendly text like 'SKU' instead of 'sku'.

    Helper Text

    Add contextual hints below form fields using the hint parameter to guide users with important information.

    Field Types

    Control input types with the 'as' parameter to optimize user experience, such as using string inputs for decimal values.

    Customizing Product Images

    Now comes the exciting part—integrating image uploads and creating dynamic, responsive product displays that automatically handle sizing and optimization.

    1. Add the image upload field to your form (around line 21):

      form do |f|
         f.inputs "Product Details" do
            f.input :title
            f.input :sku, label: "SKU", hint: "A unique SKU for this product. Very Important!"
            f.input :image, as: :file
            f.input :price, as: :string
            f.input :description
            f.input :specs
         end
    2. Save and reload the Edit Product page—you'll see the file upload field has appeared!

    3. Before we can use this form, we need to configure Active Admin's security parameters. Examine the commented code in products.rb for context.

      Rails implements strong parameters as a security mechanism, requiring explicit permission for form fields to prevent unauthorized data manipulation. We must configure Active Admin to accept our new image field through the permit_params directive.

    4. Replace all commented code with this security configuration:

      ActiveAdmin.register Product do
         permit_params :title, :description, :specs, :sku, :price, :image
      
         form do |f|
    5. Save the file and reload the Edit Product page.

    6. Click Choose File or Browse next to the Image field.

    7. Navigate to: Desktop > Class Files > yourname-Rails Level 2 Class > That Nutty Guy HTML > img > product_images.

    8. Select the appropriate image for the product you're editing.

    9. Click Update Product at the bottom of the form.

      You'll be redirected to the product's show page. The image isn't visible yet because we need to customize the display template—let's fix that next.

    10. Return to products.rb and add a custom show page configuration:

      permit_params :title, :description, :specs, :sku, :price, :image
      
      show do
         attributes_table do
         end
      
      form do |f|
    11. Define which product attributes should appear in the show page:

      show
         attributes_table do
            row :title
            row :sku
            row :price
            row :description
            row :specs
         end
         active_admin_comments
      end

      The active_admin_comments method preserves Active Admin's built-in commenting functionality for collaborative admin workflows.

    12. Add the image display at the top of the attributes table:

      attributes_table do
         row :image
         row :title
         row :sku
         row :price
         row :description
         row :specs
      end
    13. Save and reload the product page. You'll see "image" text appears, but we need to render the actual image.

    14. Update the image row with proper display logic:

      attributes_table do
         row :image do
           image_tag url_for(product.image) if product.image.attached?
         end
         row :title
         row :sku
         row :price
         row :description
         row :specs
      end

      When you need custom content in a table row, wrap it in a do…end block. The url_for() helper converts Active Storage's file object into a browser-accessible URL, while the conditional check ensures we only display images when they exist.

    15. Save and reload—your image now appears! However, it's likely enormous and needs resizing.

    16. Implement responsive image sizing using Active Storage's variant system:

      image_tag url_for(product.image.variant(resize: "200x200"))

      Reload the page to see your properly-sized image. The variant method leverages ImageMagick to create optimized image versions on-demand. The resize parameter maintains aspect ratio while fitting the image within the specified dimensions, preventing distortion.

    17. Now let's replace the static product images in the main catalog. Open nutty > app > views > products > index.html.erb

    18. Locate this static image tag around line 9:

      <img id="bill" class="" src="img/product_images/tinfoil-hat.jpg" ALT="Tinfoil Hat"></a>
    19. Replace it with dynamic image rendering:

      <%= link_to product do %>
         <% if product.image.attached? %>
                <%= image_tag url_for(product.image.variant(resize_to_limit: [500,500])), ALT: product.title %>
              <% end %>
         <p class="product"><%= product.title %></p>
      <% end %>
    20. Save the file and navigate to localhost:3000 in your browser.

      Excellent! The static placeholder images are gone, replaced by the actual product image you uploaded. The other products will display their images once you upload them in subsequent exercises.

    21. Click on your edited product to view its individual page—notice we still need to update this view as well.

    22. Open nutty > app > views > products > show.html.erb and locate this static image tag around line 17:

      <img class="product" src="/img/product_images/tinfoil-hat.jpg" ALT="<%= @product.title %>">
    23. Replace it with responsive image display code:

      <div class="visible-xs">
            <h1><%= @product.title %></h1>
            <p class="gray-text">Item #<%= @product.sku %></p>
            <hr>
         </div> 
         <% if @product.image.attached? %>
         <%= image_tag url_for(@product.image.variant(resize_to_limit: [700,900])), ALT: @product.title, class: 'product' %>
         <% end %>
      </div> <!—/column1—>
    24. Save the file and reload the product page to see your properly-sized, dynamic product image in action!

    25. Keep both browser tabs open and the server running—we'll build upon this image management system in the next exercise.

    Image Resize Methods

    Featureresizeresize_to_limit
    BehaviorFits within exact dimensionsRespects maximum dimensions
    Aspect RatioMaintained without distortionOriginal proportions preserved
    Use CaseAdmin thumbnails (200x200)Product displays (700x900)
    Recommended: Use resize for consistent thumbnail sizes and resize_to_limit for flexible product displays

    Image Integration Workflow

    1

    Upload via Admin

    Use the file input field to upload product images through the Active Admin interface

    2

    Configure Display

    Set up show page with attributes_table and image_tag helpers for proper image rendering

    3

    Replace Static Images

    Update view templates to use dynamic Active Storage URLs instead of hardcoded image paths

    Key Takeaways

    1Active Storage provides robust file attachment capabilities optimized for both cloud and local storage solutions
    2ImageMagick and the image_processing gem enable automatic image resizing and transformation features
    3Arbre markup language allows extensive customization of Active Admin forms with labels, hints, and field types
    4Strong parameters (permit_params) are essential for security when allowing file uploads through admin interfaces
    5Image variants support multiple resize methods: resize for exact dimensions and resize_to_limit for maximum constraints
    6Dynamic image URLs replace static image paths using url_for helper with Active Storage attachment objects
    7The has_one_attached association in models establishes the connection between records and their image attachments
    8View templates require conditional checks (image.attached?) to handle products with and without uploaded images

    RELATED ARTICLES