DEV Community

Cover image for New way of bundling JS/CSS in Rails
Abeid Ahmed
Abeid Ahmed

Posted on

New way of bundling JS/CSS in Rails

Apologies for misleading you with a vague title, but I wouldn't call this approach the new way, because under the hood webpacker and sass-rails were doing all of the bundlings without us needing to tinker with the configuration. For simpler applications, this was rock solid, but for complex applications, webpacker kind of stood in the way. But all is good now since the advent of two gems jsbundling-rails and cssbundling-rails.

So let's see what these gems have to offer. We'll start with a bare-bone version of rails without any JavaScript.

rails new myapp --skip-javascript
Enter fullscreen mode Exit fullscreen mode

After initializing the rails app, go to the Gemfile and add these two gems that we'll be using.

gem 'jsbundling-rails'
gem 'cssbundling-rails'
Enter fullscreen mode Exit fullscreen mode

Run bundle install after that.

In this demo, we'll be using webpack and tailwindcss as the example.

./bin/rails javascript:install:webpack
./bin/rails css:install:tailwind
Enter fullscreen mode Exit fullscreen mode

These generators should create a Procfile.dev, webpack.config.js, and more. Go to the app/javascript/application.js and write some JavaScript code. Now run ./bin/dev and with any luck, your JavaScript and CSS should be bundled and compiled into app/assets/builds directory into two files, namely: application.css and application.js. This is so much better because now we can use

<%= stylesheet_link_tag "application", "data-turbo-track": "reload", media: "all" %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
Enter fullscreen mode Exit fullscreen mode

into our layouts/application.html.erb file.

Having webpack.config.js in our root directory is even better. It's all plain vanilla webpack without any fancy wrappers around it and hence we can customize it to our hearts.

However, the above-mentioned approach to using tailwind in our app is limited. For example, we cannot use @layer or any other fancy tailwind syntax because it depends on postcss and other jazz. Again, for simpler applications it's fine, but a complex one may need those syntaxes and may need to write scss instead of css alone.

Although we can revert the changes that we made above easily, for ease of understanding let's spin up a new rails app instead.

rails new myapp2 --skip-javascript
Enter fullscreen mode Exit fullscreen mode

This time we'll only be installing the jsbundling-rails gem.

./bin/rails javascript:install:webpack
Enter fullscreen mode Exit fullscreen mode

As usual, the generator will create the Procfile.dev, webpack.config.js, and more.

So in this approach to installing TailwindCSS, we'll be installing postcss and its plugins to handle all situations.

yarn add tailwindcss autoprefixer css-loader mini-css-extract-plugin postcss postcss-import postcss-loader
Enter fullscreen mode Exit fullscreen mode

That was quite a lot. But you can add or remove these libraries depending on your application's need.

Our approach will be the same. We'll output two files, namely: application.css and application.js in the app/assets/builds directory so that we can use the stylesheet_link_tag and javascript_include_tag in our layout/application.html.erb file.

Go to the app/assets/stylesheets/application.css and rename this file to application.scss, and also paste the following lines of code.

@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
Enter fullscreen mode Exit fullscreen mode

Let's configure our webpack.config.js

const path = require('path')
const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  mode: 'production',
  entry: {
    application: [
      './app/javascript/application.js',
      './app/assets/stylesheets/application.scss',
    ],
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'app/assets/builds'),
  },
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
    }),
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1,
    }),
  ],
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we have defined two entry points inside the application array. If you have separate assets for your admin page or a marketing page, you can define another array of entry points and webpack will spit out the compiled assets into separate files.

Also, take note of the plugins array. The MiniCssExtractPlugin is responsible for compiling the stylesheet and then spitting it out into the defined directory.

Now the final piece of the puzzle is to configure postcss and manifest.js. Create a file called postcss.config.js in the root of the project directory and write down the following lines.

module.exports = {
  plugins: [
    require('postcss-import'),
    require('tailwindcss'),
    require('autoprefixer'),
  ],
}
Enter fullscreen mode Exit fullscreen mode
// app/assets/config/manifest.js

//= link_tree ../images
//= link_tree ../builds
Enter fullscreen mode Exit fullscreen mode

Now run ./bin/dev and check the app/assets/builds directory for your compiled assets. It wasn't that hard, was it.

I'm excited about where rails is heading to. We started with hotwire and now we have new asset bundling options. I wonder what's next. Now is the time to hop into the rails ecosystem if you haven't already.

Cover picture: https://unsplash.com/photos/cvBBO4PzWPg

Top comments (2)

Collapse
 
elmassimo profile image
Máximo Mussini

If you like a faster development experience that doesn't require reloading the page after each change, you can try Vite Rails.

Vite uses esbuild and rollup, making it extremely fast in development, and very efficient in production.

Collapse
 
adrianvalenz profile image
Adrian Valenzuela

I'd be curious to see how TailwindCSS is implemented in Rails with esbuild only.