Mastering Packs in Webpacker
Tags: rails, webpacker
Published: 2019-09-25 18:43:36

---
title: Mastering Packs in Webpacker
published: true
tags: rails, webpacker
canonical_url: https://prathamesh.tech/2019/09/24/mastering-packs-in-webpacker/
---

In the [previous article](https://dev.to/prathamesh/understanding-webpacker-in-rails-6-l4i), we saw what Webpacker is and how Rails 6 has integrated it. In this article, we will understand how to use the **packs.**

A new Rails 6 app creates following files under `app/javascript`, t_he new destination for writing our JavaScript code_.

<!--kg-card-begin: code-->
```
Projects/scratch/better_hn master ✗ 2.6.3 ◒
▶ tree app/javascript
app/javascript
├── channels
│   ├── consumer.js
│   └── index.js
└── packs
    └── application.js

2 directories, 3 files
```
<!--kg-card-end: code-->

The packs directory contains the **_entry points_** for webpack to start the compiling process. The content of this file in a new Rails 6 app are as follows.

<!--kg-card-begin: code-->
```
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
```
<!--kg-card-end: code-->

> The entry points are analogues to the `app/assets/application.js` file generated by asset pipeline.

The `require` statement here is not same as the _require directive_ from the asset pipeline. This `require` statement can require the NPM packages as well as local modules from our code. For eg. in this case, the first three lines above require three NPM packages - Rails UJS, Turbolinks and Active Storage whereas the last line requires `app/javascript/channels/index.js`.

> Webpack has a convention for looking for `index.js` file under the directory name we are trying to require.

Main difference between the way requiring code between Webpacker and asset pipeline is that we don't use the directives in Webpacker packs. We just directly call `require` or `import` with the package names or directory names.

<!--kg-card-begin: code-->
```
// app/javascript/packs/application.js

import React from 'react'
import ReactDOM from 'react-dom'

```
<!--kg-card-end: code-->

The pack file can also have actual JavaScript code related to the application but it is good practice to keep the pack files clean only with minimal code importing the modules and packages and keeping the actual application related code outside of `app/javascript/packs`.

> Keep pack files minimal and import the actual code in it.

So for a React project, we will just add our base component to the DOM in the pack file and manage rest of the components under `app/javascript` outside of the `pack` directory.

<!--kg-card-begin: code-->
```
// app/javascript/packs/application.js
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import HelloWorld from '../components/hello'

document.addEventListener('DOMContentLoaded', () => {
  ReactDOM.render(
    <HelloWorld name="Webpack" />,
    document.body.appendChild(document.createElement('div')),
  )
})

// app/javascript/components/hello.js

const HelloWorld = props => (
  <div>Hello {props.name}!</div>
)

Hello.defaultProps = {
  name: 'David'
}

Hello.propTypes = {
  name: PropTypes.string
}

module.exports HelloWorld
```
<!--kg-card-end: code-->

Webpacker is pretty liberal in terms of how one should organize their JavaScript code. The only rule is that `packs` directory is special and is treated as entry points by Webpacker. Rest is up to you. I prefer adding a `components` directory for all the React components. My application has `application` pack as the default pack and `admin` pack which has code only related to the admin users.

In the Sprockets world, we have to add every custom file that we want Rails to precompile, in the asset precompile list.

<!--kg-card-begin: code-->
```
Rails.application.config.assets.precompile += %w[admin.js]
```
<!--kg-card-end: code-->

But because the whole `packs` directory is considered as entry points by Webpacker, we don't have to add any of the custom packs to the precompile list. It just works!

In general, I will say that we should treat the `app/javascript` directory as an _application within application_ itself and organize the code accordingly. For eg. in one of my application, the directory structure of the `app/javascript` is as follows.

<!--kg-card-begin: code-->
```
app/javascript
├── admin
├── channels
├── login
└── packs
    ├── admin.js
    ├── application.js
    └── login.js
```
<!--kg-card-end: code-->

When we compile this JavaScript, the output looks like following.

<!--kg-card-begin: code-->
```
▶ ./bin/webpack-dev-server
ℹ ｢wds｣: Project is running at http://localhost:3035/
ℹ ｢wds｣: webpack output is served from /packs/
ℹ ｢wds｣: Content not from webpack is served from /Users/prathamesh/Projects/scratch/better_hn/public/packs
ℹ ｢wds｣: 404s will fallback to /index.html
ℹ ｢wdm｣: Hash: 5387bbdba96d7150c792
Version: webpack 4.39.2
Time: 2753ms
Built at: 09/24/2019 12:23:20 AM
                                     Asset Size Chunks Chunk Names
          js/admin-67dd60bc5c69e9e06cc3.js 385 KiB admin [emitted] admin
      js/admin-67dd60bc5c69e9e06cc3.js.map 434 KiB admin [emitted] admin
    js/application-d351b587b51ad82444e4.js 505 KiB application [emitted] application
js/application-d351b587b51ad82444e4.js.map 569 KiB application [emitted] application
          js/login-1c7b2341998332589ec0.js 385 KiB login [emitted] login
      js/login-1c7b2341998332589ec0.js.map 434 KiB login [emitted] login
                             manifest.json 958 bytes [emitted]
```
<!--kg-card-end: code-->

Apart from generating the fingerprinted files and source map files, it also generates a `manifest.json` which lists information about all the files generated by the compilation process. Rails uses this file to convert references to the assets in the `javascript_pack_tag` to the actual compiled files. For eg.`javascript_pack_tag('admin')` will be converted to `js/admin-67dd60bc5c69e9e06cc3.js`. A sample `manifest.json` looks like [this](https://gist.github.com/prathamesh-sonpatki/25c8657fc811680af37cf79fcb3c3792).

Now that we have the packs ready, we will use them as per our requirements in the layout files. In my case, the `login` pack is only used in `login` layout and is separate from `application` pack which is used once the user is logged in. For `admin` layout, apart from `application` pack, a separate `admin` pack is used. We can use any of the packs by including it in the layout file.

<!--kg-card-begin: code-->
```
<body>
  <%= javascript_pack_tag "application" %>
  <%= javascript_pack_tag "admin" %>
</body>
```
<!--kg-card-end: code-->

So to summarize:

- Keep pack files minimal and just import the required code from other files.
- Only pack files must go in `app/javascript/packs`
- You are free to organize the rest of the JavaScript code as per your wish in the `app/javascript`. 
- Keep an eye on the output of Webpack to monitor the bundle size.
- Organize pack files as per your requirements and manage the packs depending on the features they will serve.

If you are interested in knowing more about Webpacker and Rails 6, [be with me on the Road to Rails 6](https://prathamesh.tech/road-to-rails-6/).