Using Rails 6, where do you put your “page specific” JavaScript code?

Let's go through the contents of this directory in a empty Rails 6 application.

▶ tree app/javascript
app/javascript
├── channels
│   ├── consumer.js
│   └── index.js
└── packs
    └── application.js

2 directories, 3 files

the packs directory is significant for us so let's see what it contains.

// app/javascript/application.js
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")

webpack has a concept of entry points which are the files that it looks for first when it starts compiling your JavaScript code.

Webpacker gem creates the application pack in the form of this application.js file under app/javascript/packs. If you remember the assets pipeline, this file is equivalent to the app/assets/javascripts/application.js file

Simple example

How to Add Javascript to Ruby on Rails (Adding Javascript as a Module):

  1. create folder in Javascript path app/javascript ex: 'post'

  2. add your javascript files in folder like index.js

  3. add postmodule in app/javascript/application.js -> require('post')

    require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require("post")

Let's go through the contents of this directory in a Rails 6 application after add post module

▶ tree app/javascript
app/javascript
├── channels
│   ├── consumer.js
│   └── index.js
└── packs
|    └── application.js
|ـــ post
     |__ index.js

This simple way to use webpack same old rails style.


I'll describe a few options in order of increasing level of difficulty with regards to experience with Webpack and Webpacker.

  1. Forget page-specific JavaScript and put everything in the application.js bundle. This will most definitely be the easiest approach to understand. For a small application, the tradeoff may well be worth it as having an application that's easier to maintain may outweigh added cost of learning how to best to split up your JS code with Webpack with little performance gain.

  2. Use dynamic imports to lazy load your page-specific code. In this scenario, you would still place all your JavaScript within the application.js dependency graph, but not all of this code would be loaded up-front. Webpack recognizes the dynamic import() function when it compiles your JS and will split the imported chunk out into a separate file that can be loaded on-demand in the browser using a JS framework router or a simple trigger.

    For example, you could have code that looks like this:

    if (document.querySelectorAll(".post-index").length) {
      import("./post/index") // webpack will load this JS async
    }
    
  3. Use page-specific "packs" combined with the splitChunks configuration API. In this scenario, instead of using an application.js pack, you would construct one for each page you want to treat differently, e.g, posts.js, admin.js etc. Using the splitChunks plugin means that your bundles can properly share code. I highly recommend treading carefully with this approach until you understand how Webpack works OR be willing to go through the process of learning Webpack in choosing this path. Webpack typically works best on the assumption you use only one entry point per page, otherwise, you may end up duplicate code across bundles unless you know what you're doing.