Adding Custom JavaScript in Rails 6
Tags: rails, javascript
Published: 2019-08-21 09:38:53

In Rails 6, JavaScript assets are no longer included with the `app/assets/` directory and instead have been moved into a separate directory `app/javascript` handled by Webpacker. That's great, but when I wanted to add some custom javascript of my own, there wasn't any clear documentation that described how it should be done.

After some experimentation and digging around on the internet, here are a couple of methods that seem to work. Note that I'm not a javascript expert by any means, so if there is a better way—or other ways that I'm missing—let me know in the comments!

# Method 1: Create a custom directory and require it in `application.js`
If you look at `application.js`, the comment at the top suggests that you "place your actual application logic in a relevant structure within app/javascript and only use these pack files to reference that code so it'll be compiled."


```javascript
// app/javascript/packs/application.js

// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.

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

You can set this up by adding a custom directory within the `app/javascript/`, e.g. `custom/`, and then require the files within in `application.js`.

For example, if you have a javascript file named `home.js` in the `app/javascript/custom/` directory, you can get it to load with `require()`:

```javascript
// app/javascript/packs/application.js

// ...

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

require("custom/home")
```

Since it's required in `application.js`, the custom javascript will be compiled along with all the other javascript into a timestamped `application.js` file that looks something like `application-a03d1868c0a3f825e53e.js`.

This is loaded by the `<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>` tag in the `app/views/layouts/application.html.erb`, automatically created when you make a new Rails app.

For a cleaner look, you could also name the javascript file `index.js` and require it with a simple reference to the directory like `require("custom")` because `require()` looks for an `index.js` file when given a path to a directory (if there is no `index.js`, it will fail).

# Method 2: Add custom JavaScipt within the `app/javascript/packs` directory

If for some reason you don't want to create another directory, you could opt to add custom javascript files within the `app/javascript/packs` directory. Then, require the files in `application.js` with `require()`.

For example, if you have a file named `custom.js` in `app/javascript/packs`, require it like below and it will be compiled into the timestamped `application.js` file with all the other javascript:

```javascript
// app/javascript/packs/application.js

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

require("packs/custom")
```

# Method 3: Using `javascript_pack_tag` for separate JavaScript files (packs)

If you don't want your custom javascript to be compiled into the `application.js` with everything else, you can get Rails to compile it into a separate file, or "pack."

To do so, add a custom file within `app/javascript/packs`, e.g. `custom.js`, then use the `javascript_pack_tag` helper where the file is needed in the views like so: `<%= javascript_pack_tag 'custom' %>`

The custom javascript will be compiled *separately* from the rest of the javascript into a timestamped `custom.js` that looks something like this: `custom-a03d1756c0a3f825e53e.js`

Here's how it might look if you added the custom javascript just before the ending `body` tag in the `layouts/views/application.html.erb`:

```erb
# app/layouts/views/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title>My App</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body class="<%= controller_name %> <%= action_name %>">
    <%= render 'layouts/header' %>

    <%= yield %>

    <%= render 'layouts/footer' %>
    
    <%= javascript_pack_tag 'custom' %>
  </body>
</html>
```

Thanks for reading!