Have you ever struggled to understand how your Rails application delivers assets? If yes then this blog post is for you. come let's get into it.
So what are assets in the context of a web app?
Assets in your application are supplementary files that the browser requests after the initial chunk of HTML is loaded. These assets include CSS stylesheets, JavaScript files, images, videos, and more.
What is the Asset Pipeline?
The asset pipeline provides a framework to handle the delivery of JavaScript and CSS assets.
When you create a new rails app using below command:
rails new my-demo-app
By default, you will get sprockets-rails for CSS and importmap-rails for JavaScript processing. You can find these gems in your Gemfile.
Features of Asset Pipeline
- Adding fingerprints to the assets, so that the file is cached by the web browser and CDN.
For example, If your app has a stylesheet named navbar.css. It will insert a SHA256 fingerprint to that file like below.
navbar-908e25f4bf641868d8683022a5b62f54.css
You can invalidate the cache by altering this fingerprint, which happens automatically whenever you change the file contents.
JavaScript processing using import maps.
Concatenate all CSS files into one main .css file, which is then minified or compressed.
It allows coding assets via a higher-level language for CSS.
Import maps:
Import maps let you import JavaScript modules using logical names that map to versioned/digested files – directly from the browser.
Enough of theory, let's dive into a simple example to see how it works all together
Create one new rails app using the below commands, If not created already.
rails new my-demo-app
cd my-demo-app
rails s
Now in our new application, we are going to add bootstrap library.
Importmaps provide a way to pin JavaScript modules and load them in your application directly from a CDN or locally.
Adding bootstrap and popperjs using jspm cdn link.
config/importmap.rb:
# Pin npm packages by running ./bin/importmap
pin "application"
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"
pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@5.3.3/dist/js/bootstrap.esm.js"
pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js"
We can add any npm packages using the below command as well, for eg.
bin/importmap pin react react-dom
vendor/javascript folder is the place where downloaded JS packages would be stored.
Then, import the package into your application.js as usual
app/javascript/application.js:
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"
import "bootstrap"
import "@popperjs/core"
To add bootstrap css into our app, copy all the code from bootstrap cdn then create one new file bootstrap.css under vendor/css folder and paste in it. Now we need to add this to our asset load path.
config/initializers/assets.rb:
# Be sure to restart your server when you modify this file.
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = "1.0"
# Add additional assets to the asset load path.
Rails.application.config.assets.paths << Rails.root.join("vendor", "css")
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
Then rename application.css to application.scss and import bootstrap there like below.
app/assets/stylesheets/application.scss:
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/
@import "bootstrap";
application.scss serves as the starting point where you import all the other stylesheets that make up our application's styles.
Sprockets on its own is not able to transpile the Sass into CSS. So we need a sass engine to transpile our sass to regular css. Add this gem in your Gemfile.
gem 'dartsass-sprockets'
then run bundle install
command.
Now uncomment root path line your routes.
config/routes.rb:
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
get "up" => "rails/health#show", as: :rails_health_check
# Defines the root path route ("/")
root "posts#index"
end
Under app/controllers folder, create one new file and paste the below code.
posts_controller.rb:
class PostsController < ApplicationController
def index
end
end
Create one new view file at app/views/posts and paste the following html code in it:
index.html.erb:
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">My Blog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5">
<div class="row">
<div class="col-md-8">
<div class="blog-post mb-4">
<h2 class="blog-post-title">Blog Post Title 1</h2>
<p class="blog-post-meta">June 23, 2024 by <a href="#">Author</a></p>
<p>This is a paragraph from the first blog post. It contains some interesting content that readers might enjoy.</p>
<hr>
</div>
</div>
<div class="col-md-4">
<div class="p-4 mb-3 bg-light rounded">
<h4 class="fst-italic">About</h4>
<p class="mb-0">This is a simple blog page example using Bootstrap 5.3. Customize it as you like!</p>
</div>
</div>
</div>
</div>
<footer class="blog-footer text-center py-3 bg-light">
<p>Blog template built with <a href="https://getbootstrap.com/">Bootstrap</a>.</p>
</footer>
Create one new css file under app/assets/stylesheets. This is our custom stylesheet and paste the following css code.
posts.css:
body {
font-family: Arial, sans-serif;
}
.navbar-brand {
font-weight: bold;
}
.blog-post-title {
color: #007bff;
margin-bottom: 0.5rem;
}
.blog-post-meta {
color: #6c757d;
margin-bottom: 1rem;
}
.blog-footer {
margin-top: 2rem;
border-top: 1px solid #e5e5e5;
}
.container {
max-width: 960px;
}
h4.fst-italic {
font-style: italic;
}
.p-4 {
padding: 1.5rem !important;
}
.p-4 .list-unstyled {
padding-left: 0;
list-style: none;
}
Finally, start our rails server using rails s
to see things in action.
Now view your page source using Ctrl+U, all your application stylesheets concatenated into one big css file application.css and fingerprint is added as well.
Also our npm packages added successfully, see in the <script type="importmap" data-turbo-track="reload"></script>
tags. The big advantage of this approach is it will eliminate the need for Webpack, yarn, node or any other part of the JavaScript toolchain.
To learn more in detail about asset pipeline
Thanks for reading this post, will come back with another way of handling javascript in the rails app.
Top comments (4)
Thanks!
Thank you @z2flow
Excellent article. Thanks a lot !
Thank you @nemuba