In this article, we'll look at how to write and manage CSS in Ruby on Rails 7 applications.
There are quite a few options available, so we'll break this down in the following sections:
- How to use plain CSS with sprockets
- How to use third-party stylesheets
- How to use SASS in Rails
- How to use Tailwind in Rails
- How to use Bootstrap in Rails
- How to use Propshaft in Rails
- Note on deploying
How to use plain CSS with sprockets
If you just want to write pure CSS, you can do it out of the box with the help of sprockets (one half of the Rails Asset pipeline; the other half is importmap, which deals with JavaScript).
This is the default setup for a Rails app.
Sprockets takes care of concatenating, minifying and digesting all of your app's CSS files.
By default, it can only handle plain CSS, not SASS or anything else that requires transpiling or compiling.
It will load any CSS from the app/assets/stylesheets directory (not including subdirectories).
A typical Rails 7 app's application layout file will link app/assets/stylesheets/application.css like this:
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
If you have a stylesheet called custom.css in the stylesheets directory, you can use it by requiring it in application.css.
/* *= require_tree . *= require_self *= require custom */
You can also have controller-specific stylesheets. To do it for all controllers add this line to your layout file:
<%= stylesheet_link_tag params[:controller] %>
For a controller called ProductsController, that will include the corresponding products.css file only on that controller's pages.
But be careful using this universal controller link tag. If you don't have a corresponding CSS file for every controller, you'll get an error when you load those pages.
You'd be better off using a specific tag for the controllers which have their own stylesheets in the corresponding layouts.
So you could add this to app/views/layouts/products.html.erb:
<%= stylesheet_link_tag "products" %>
How to use third-party stylesheets
If you want to use a third-party stylesheet, you can put it in the vendor/assets/stylesheets directory and it will automatically be available.
For example, Simple.css is a CSS framework that provides a CSS file that makes semantic HTML look good without you having to add any classes.
To use it in your Rails app, you can download it and save it in the vendor/assets/stylesheets directory.
Then require simple in app/assets/stylesheets/application.css:
/* *= require_tree . *= require_self *= require simple */
How to use SASS in Rails
If you want more powerful features to customise your CSS, you can use SASS with the help of one of many gems.
Rails used to ship with the sass-rails gem, which provided support for writing SASS out of the box. That is no longer the case. Plus, sass-rails is no longer maintained.
So you need another gem.
How to use SASS with the dartsass-rails gem
The official recommended gem is dartsass-rails. It's a wrapper around the sass utility written in Dart. dartsass-rails is maintained by the Rails organisation.
It requires running a separate process to convert SASS into CSS. In development, this is done by running a watch process in addition to the Rails server. In production, the build process is automatically attached to assets:precompile.
Add the gem to your Gemfile and install it:
./bin/bundle add dartsass-rails
Then run the installer:
./bin/rails dartsass:install
That installs the foreman gem and creates a Procfile.dev file which contains this:
web: bin/rails server -p 3000 css: bin/rails dartsass:watch
foreman runs those two processes - the Rails server and dartsass for processing .scss files. It's run in watch mode, so any changes to the .scss files are picked up immediately and processed automatically.
The output CSS files are stored in app/assets/builds/, which is also linked to from the manifest.js file.
A new file app/assets/stylesheets/application.scss is created, where you can write your SASS code.
By default, this gem only builds application.scss. If you want to change or add to this, you need to modify Rails.application.config.dartsass.builds. For example:
Rails.application.config.dartsass.builds = { "app/index.sass" => "app.css", "site.scss" => "site.css" }
When you run bin/dev, it starts the Rails server and dartsass:watch:
$ bin/dev 07:06:36 web.1 | started with pid 39159 07:06:36 css.1 | started with pid 39160 07:06:38 web.1 | => Booting Puma 07:06:38 web.1 | => Rails 7.1.3 application starting in development 07:06:39 css.1 | Sass is watching for changes. Press Ctrl-C to stop. 07:06:39 css.1 |
When you make a change to application.scss and save it, you'll see a compilation confirmation in the logs:
07:10:17 css.1 | [2024-02-01 07:10:17] Compiled app/assets/stylesheets/application.scss to app/assets/builds/application.css.
How to use SASS with the dartsass-sprockets gem
The other option for using SASS in Rails is the dartsass-sprockets gem.
The benefit of using this gem over dartsass-rails is that it does not require a separate utility or process (and so no foreman). As the name suggests, it works directly through sprockets. Once you install the gem, you can directly write SASS (in files with the .scss. extension) and it will just work.
Install the gem:
bundle add dartsass-sprockets
This will automatically configure your default Rails config.assets.css_compressor to use :sass.
You'll need to rename your application.css file to application.scss and you're all set.
How to use SASS with the cssbundling-rails gem
One more option for using SASS in Rails is the cssbundling-rails gem. It uses Nodejs (via yarn) to process the SASS.
Install the gem:
bundle add cssbundling-rails
Run the installer:
bin/rails css:install:sass
This installer adds some things similar to those made by dartsass-rails (Procfile.dev, bin/dev, foreman). The difference is that it also adds a package.json file and installs the npm package sass with yarn.
The css build process in Procfile.dev uses yarn:
web: env RUBY_DEBUG_OPEN=true bin/rails server css: yarn build:css --watch
It also creates a app/assets/stylesheets/application.sass.scss file which is the entry point for your SASS code.
Builds go in app/assets/builds.
Using cssbundling-rails makes sense if you're also using Node to process JavaScript. Otherwise, you take on the complexity and weight of a JS runtime for not much benefit.
How to use Tailwind in Rails
Tailwind is a utility-first CSS framework and a popular choice for Rails apps.
How to use Tailwind with the tailwindcss-rails gem
If you want to use Tailwind, the easiest option is the tailwindcss-rails gem (also recommended in the official Tailwind docs).
It's a wrapper around the standalone executable version of the Tailwind CSS framework. It does not require Node.js.
In development, it runs the Tailwind executable in watch mode. In production, the build step is automatically attached to assets:precompile, so before the asset pipeline digests the files, the Tailwind output is generated.
Add the gem to your Gemfile and install it with:
./bin/bundle add tailwindcss-rails
Then run the installer:
./bin/rails tailwindcss:install
This installer also adds some things similar to those made by dartsass-rails (Procfile.dev, bin/dev, foreman).
Procfile.dev uses tailwindcss for the CSS build process:
web: bin/rails server css: bin/rails tailwindcss:watch
It adds tailwind CSS to the application layout:
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
Note that this goes above the link tag for application.css.
It also adds app/assets/stylesheets/application.tailwind.css, which is where you import Tailwind plugins and setup custom @apply rules.
The Tailwind config file is added at config/tailwind.config.js.
When you run bin/dev you can see the two processes in the logs:
10:34:23 web.1 | started with pid 43568 10:34:23 css.1 | started with pid 43569
Load your app in the browser and you'll notice at least one change. The font will now be Inter (the default font of Tailwind).
Add some Tailwind classes to a view file, save and the logs will show something like this:
10:38:21 css.1 | 10:38:21 css.1 | Rebuilding... 10:38:21 css.1 | 10:38:21 css.1 | Done in 96ms.
The tailwind watch process automatically rebuilds and puts the built file tailwind.css in app/assets/builds.
How to use Tailwind with the cssbundling-rails gem
You can also use Tailwind with the cssbundling-rails gem.
Install the gem:
bundle add cssbundling-rails
Run the installer:
bin/rails css:install:tailwind
Everything is pretty much the same as with the tailwindcss-rails gem, except for the addition of a package.json file, which installs three npm packages: autoprefixer, postcss and tailwindcss.
You can see in Procfile.dev, it uses yarn to build the css: css: yarn build:css --watch
Run bin/dev to start the Rails server and the yarn build process.
Make a change to a template file. Add a tailwind class, save and see the build confirmation in the logs: 16:39:58 css.1 | 16:39:58 css.1 | Rebuilding... 16:39:58 css.1 | 16:39:58 css.1 | Done in 68ms.
It's simple and it works, with the additional dependency of running Node.
How to use Bootstrap in Rails
Bootstrap may not be the cool kid on the block any more, but it's still very popular.
How to use Bootstrap with the bootstrap-rubygem gem
The easiest option to use Bootstrap in Rails is the bootstrap-rubygem gem.
Add bootstrap to your Gemfile and install it:
bundle add bootstrap
If you have an existing application.css file, you'll need to rename it to application.scss (note the .scss extension).
Remove all the *= require and *= require_tree statements. Instead, use @import to import Sass files.
Import Bootstrap styles with:
@import "bootstrap";
This gem requires a SASS preprocessor, so you'll need a gem for that. Pick one of dartsass-rails or dartsass-sprockets, depending on whether you care about running a separate process in devleopment.
At the time of writing this article, the bootstrap gem has only recently added support for dartsass-rails and it hasn't been released on rubygems yet. You'll need to install it directly by specifying the Github repo as the source in your Gemfile.
So you might want to use dartsass-sprockets instead to keep things simple.
Then run bin/dev to start the Rails server and the sass engine.
One downside of this gem is that it also requires a JS runtime. On your development machine, you most likely already have Node, so you may not realise this until you try to use this gem with the default Rails Dockerfile or deploy it on a server without node. You will run into this error: ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime.
So you need to either install Node or a gem like mini_racer.
How to use Bootstrap with the cssbundling-rails gem
You can also use Bootstrap with the cssbundling-rails gem.
The setup process is exactly the same as we saw earlier for SASS and Tailwind.
Install the gem:
bundle add cssbundling-rails
Run the installer specifying bootstrap: bin/rails css:install:bootstrap
It adds bootstrap and some dependent npm packages to package.json.
Run bin/dev to start the Rails server and the yarn build process.
How to use Propshaft in Rails
Propshaft is a new asset pipeline library for Rails. It's not the default yet, but it will replace Sprockets as the default in Rails 8.
It's a much simpler library than sprockets. It assumes assets will be processed and bundled by Node.js bundlers or served directly to browsers.
To use Propshaft in a new Rails 7 app, run:
rails new myapp -a propshaft
If you have an existing app using sprockets and SASS with one of the sass gems, you will need to remove that gem and use cssbundling-rails instead. See the official migration README for the details.
Propshaft itself only supports plain CSS. You can write CSS in application.css and any other CSS files in app/assets/stylesheets.
You can also add assets from other directories by configuring config.assets.paths.
You can use @import statements.
For example, if you have custom.css you can import it in appplication.css:
@import url('/custom.css');
However, there's a problem with this (as of Feb 2024). In development mode, the browser will cache application.css. So your imported CSS will load the first time. But if you make changes in custom.css, they won't show up on subsequent reloads.
This is a known issue, discussed at length here - https://github.com/rails/propshaft/issues/90
If you must use imports, then you need to disable caching in development. In Chrome, you can do this by checking the "Disable cache (while DevTools is open)" checkbox.
But this is less than ideal. Hopefully, there'll be a better solution soon.
Another workaround is to avoid imports and instead include a stylesheet tag per stylesheet in your view file:
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %> <%= stylesheet_link_tag "custom", "data-turbo-track": "reload" %>
You can also use any CSS files in vendor/assets/stylesheets. Like the previous example with sprockets, if you have simple.css in that directory, you can link to it without changing any configuration:
<%= stylesheet_link_tag "simple", "data-turbo-track": "reload" %>
You can also include all stylesheets that are available with a single tag:
<%= stylesheet_link_tag :all, "data-turbo-track": "reload" %>
However, if the order of the stylesheets is important for your app, using :all may not work well.
Note on deploying
If you're deploying a default sprockets based app, it should work in production without any extra setup.
The bootstrap gem needs a JavaScript runtime (Node or mini_racer gem) and cssbundling-rails requires Node and yarn.
Make sure your server or Dockerfile is setup to install those dependencies before deploying.