Asset Pipeline
Master Rails' traditional Asset Pipeline (Sprockets) for managing CSS, JavaScript, and images in your Rails application.
Asset Pipeline Overview
# Gemfile - Asset Pipeline dependencies
gem 'sprockets-rails' # Asset pipeline
gem 'sassc-rails' # Sass support
gem 'uglifier' # JS minification
gem 'coffee-rails' # CoffeeScript support (optional)
# Asset pipeline configuration
# config/application.rb
module MyApp
class Application < Rails::Application
# Enable the asset pipeline
config.assets.enabled = true
# Asset paths
config.assets.paths << Rails.root.join('app', 'assets', 'fonts')
config.assets.paths << Rails.root.join('vendor', 'assets', 'stylesheets')
# Precompile additional assets
config.assets.precompile += %w[admin.css admin.js mobile.css]
# Asset version (for cache busting)
config.assets.version = '1.0'
end
end
Asset Pipeline Features
- Concatenation: Combines multiple files to reduce HTTP requests
- Minification: Removes whitespace and optimizes code
- Preprocessing: Supports Sass, CoffeeScript, ERB, and more
- Fingerprinting: Adds unique fingerprints for cache busting
- Gzip compression: Compresses assets for faster delivery
Asset Directory Structure
# Rails asset directory structure
app/
├── assets/
│ ├── config/
│ │ └── manifest.js # Sprockets manifest
│ ├── stylesheets/
│ │ ├── application.css # Main CSS manifest
│ │ ├── components/ # Component-specific styles
│ │ ├── pages/ # Page-specific styles
│ │ └── shared/ # Shared styles
│ ├── javascripts/
│ │ ├── application.js # Main JS manifest
│ │ ├── components/ # JS components
│ │ └── pages/ # Page-specific JS
│ └── images/ # Images and icons
vendor/
└── assets/
├── stylesheets/ # Third-party CSS
└── javascripts/ # Third-party JS
lib/
└── assets/
├── stylesheets/ # Library CSS
└── javascripts/ # Library JS
// app/assets/config/manifest.js
//= link_tree ../images
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
// Link specific files
//= link application.css
//= link application.js
//= link admin.css
//= link admin.js
CSS & Sass Integration
/* app/assets/stylesheets/application.css */
/*
* This is a manifest file that'll be compiled into application.css
*
*= require_tree .
*= require_self
*/
/* Your CSS rules go here */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
// app/assets/stylesheets/application.scss
// Sass version with imports
// Import external libraries
@import "bootstrap";
// Import your custom styles
@import "base/variables";
@import "base/mixins";
@import "base/typography";
@import "components/buttons";
@import "components/forms";
@import "components/cards";
@import "pages/home";
@import "pages/dashboard";
// Variables file: app/assets/stylesheets/base/_variables.scss
$primary-color: #007bff;
$secondary-color: #6c757d;
$font-size-base: 16px;
$line-height-base: 1.5;
// Responsive breakpoints
$mobile: 768px;
$tablet: 1024px;
$desktop: 1200px;
// Component-specific styles
// app/assets/stylesheets/components/_buttons.scss
.btn {
display: inline-block;
padding: 0.375rem 0.75rem;
margin-bottom: 0;
font-size: $font-size-base;
font-weight: 400;
line-height: $line-height-base;
text-align: center;
white-space: nowrap;
vertical-align: middle;
cursor: pointer;
border: 1px solid transparent;
border-radius: 0.25rem;
transition: all 0.15s ease-in-out;
&.btn-primary {
color: white;
background-color: $primary-color;
border-color: $primary-color;
&:hover {
background-color: darken($primary-color, 7.5%);
border-color: darken($primary-color, 10%);
}
}
}
JavaScript Management
// app/assets/javascripts/application.js
// This is a manifest file that'll be compiled into application.js
//
//= require rails-ujs
//= require turbo
//= require_tree .
// Your JavaScript code goes here
document.addEventListener('DOMContentLoaded', function() {
console.log('Application loaded');
// Initialize components
initializeNavigation();
initializeForms();
});
// Component-based organization
// app/assets/javascripts/components/navigation.js
function initializeNavigation() {
const navToggle = document.querySelector('.nav-toggle');
const navMenu = document.querySelector('.nav-menu');
if (navToggle && navMenu) {
navToggle.addEventListener('click', function() {
navMenu.classList.toggle('active');
});
}
}
// app/assets/javascripts/components/forms.js
function initializeForms() {
// Form validation
const forms = document.querySelectorAll('form[data-validate]');
forms.forEach(form => {
form.addEventListener('submit', validateForm);
});
// AJAX forms
const ajaxForms = document.querySelectorAll('form[data-remote]');
ajaxForms.forEach(form => {
form.addEventListener('ajax:success', handleAjaxSuccess);
form.addEventListener('ajax:error', handleAjaxError);
});
}
function validateForm(event) {
// Custom validation logic
const form = event.target;
const requiredFields = form.querySelectorAll('[required]');
let isValid = true;
requiredFields.forEach(field => {
if (!field.value.trim()) {
showFieldError(field, 'This field is required');
isValid = false;
}
});
if (!isValid) {
event.preventDefault();
}
}
# CoffeeScript support (optional)
# app/assets/javascripts/dashboard.coffee
class Dashboard
constructor: (@element) ->
@initializeCharts()
@bindEvents()
initializeCharts: ->
chartData = @element.dataset.chartData
# Initialize charts with data
bindEvents: ->
@element.addEventListener 'click', @handleClick.bind(@)
handleClick: (event) ->
# Handle dashboard interactions
$ ->
dashboards = document.querySelectorAll('.dashboard')
dashboards.forEach (element) ->
new Dashboard(element)
Asset Helpers
<!-- In your ERB templates -->
<!-- CSS stylesheets -->
<%= stylesheet_link_tag 'application', 'data-turbo-track': 'reload' %>
<%= stylesheet_link_tag 'admin' if admin_user? %>
<!-- JavaScript -->
<%= javascript_importmap_tags %>
<%= javascript_include_tag 'application', 'data-turbo-track': 'reload' %>
<!-- Images -->
<%= image_tag 'logo.png', alt: 'Company Logo', class: 'logo' %>
<%= image_tag 'hero-bg.jpg', class: 'hero-background' %>
<!-- Asset URLs in CSS -->
<%= asset_url('background.jpg') %>
<%= asset_path('icons/star.svg') %>
<!-- Conditional assets -->
<% if Rails.env.development? %>
<%= javascript_include_tag 'development-tools' %>
<% end %>
// Using asset helpers in Sass
.hero {
background-image: asset-url('hero-background.jpg');
background-size: cover;
background-position: center;
}
.icon-star {
background-image: asset-url('icons/star.svg');
width: 24px;
height: 24px;
}
# In controllers or helpers
class ApplicationController < ActionController::Base
private
def additional_stylesheets
stylesheets = ['application']
stylesheets << 'admin' if current_user&.admin?
stylesheets << 'mobile' if mobile_device?
stylesheets
end
end
# Custom asset helper
module AssetsHelper
def responsive_image_tag(image, options = {})
sizes = options.delete(:sizes) || {}
tag.picture do
sizes.each do |breakpoint, size|
concat tag.source(
srcset: asset_path("#{size}/#{image}"),
media: "(min-width: #{breakpoint}px)"
)
end
concat image_tag(image, options)
end
end
end
Asset Precompilation
# Precompile assets for production
rails assets:precompile
# Precompile with specific environment
RAILS_ENV=production rails assets:precompile
# Clean precompiled assets
rails assets:clean
# Clean all assets (including older versions)
rails assets:clobber
# Check asset compilation status
rails assets:environment
# config/environments/production.rb
Rails.application.configure do
# Compress CSS using a preprocessor
config.assets.css_compressor = :sass
# Compress JavaScripts using a JS compressor
config.assets.js_compressor = :terser
# Do not fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
# Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.asset_host = 'http://assets.example.com'
# Specifies the header that your server uses for sending files
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Force all access to the app over SSL
config.force_ssl = true
end
# Custom precompile configuration
# config/application.rb
class Application < Rails::Application
# Precompile additional assets
config.assets.precompile += %w[
admin.css admin.js
mobile.css mobile.js
dashboard/*.css
components/*.js
]
# Add custom paths
config.assets.paths << Rails.root.join('app', 'assets', 'fonts')
config.assets.paths << Rails.root.join('app', 'assets', 'videos')
# Asset pipeline configuration for different file types
config.assets.precompile += %w[*.png *.jpg *.jpeg *.gif *.svg]
config.assets.precompile += %w[*.woff *.woff2 *.ttf *.eot]
end
CDN & Performance
# CDN configuration
# config/environments/production.rb
Rails.application.configure do
# Asset host for CDN
config.asset_host = 'https://cdn.myapp.com'
# Or dynamic asset host
config.asset_host = Proc.new { |source|
if source.starts_with?('/assets')
"https://assets#{source.hash % 4}.myapp.com"
end
}
# CloudFront distribution
config.asset_host = 'https://d1234567890.cloudfront.net'
end
# Asset performance optimizations
# config/initializers/assets.rb
# Gzip compression
Rails.application.config.assets.configure do |env|
env.gzip = true
end
# Cache assets
Rails.application.config.assets.configure do |env|
env.cache = Sprockets::Cache::FileStore.new(
File.join(Rails.root, 'tmp', 'cache', 'assets'),
262144000, # 250MB
env.logger
)
end
# Custom asset processors
Rails.application.config.assets.configure do |env|
# Image optimization processor
env.register_processor('image/png', ImageOptimProcessor)
env.register_processor('image/jpeg', ImageOptimProcessor)
end
# Custom asset optimization
class ImageOptimProcessor
def self.call(input)
data = input[:data]
# Optimize images using ImageOptim or similar
optimized = ImageOptim.new(
pngout: false,
svgo: false,
pngcrush: { chunks: :alla, fix: false, brute: false }
).optimize_image_data(data)
{ data: optimized || data }
end
end