“How the F do I add a JS pipeline to a Jekyll website,” you ask?

I hear you, I hear you.

Let’s say you’re feeling masochistic and want to add some ES2015+ goodness to your static Jekyll site. It’s your holiday break, and you felt like learning a thing. You know, for fun.


In all seriousness, Jekyll is a decent alternative if you’re tired of the SPA life and want to jump back into static site templating.

This little tutorial makes only one assumption: that you’re storing your static files in the assets folder. Feel free to use a different folder if it fits your setup.

Also, this should be compatible with pipeline-control gems like jekyll-assets if you’re into that. Truthfully, I wrote this as an alternative because Jekyll 4 supports Sass out of the box now, and jekyll-assets isn’t compatible with it yet. Also, GitHub Pages is stuck on Jekyll v3.8.5 for the time being. :(

Initial Setup

First, create a folder called _scripts. In it, add all your JavaScript files (they can be in folders). These can be written in modern syntax (ES2015+) and even import npm packages if necessary. Since we’re writing for the web, the final output will be a iife bundle.

Although we’ll cover it in a minute, go ahead and prefix any scripts that shouldn’t be processed individually with an underscore (_). These are more-or-less your shared, private internal modules. They will be bundled into the non-prefixed script files should they be imported.

Finally, add the usual script tags to your template.

Add Rollup

Next up, create a package.json if you don’t already have one and install the needed dependencies.

Once that’s finished, make a babel.config.js file and add the following:

To process our scripts, we’ll make a rollup.config.js that greps the _scripts folder, and outputs our non-private files, each as a separate bundle, into a Jekyll-friendly assets/js folder. Explanation to follow.

What’s this doing, you ask?

  • First, create an object called inputs, where each entry is a key/value pair of the file and its relative path within the _scripts folder, respectively.
  • Next, create an outputs object by iterating over inputs and updating the values to point toward our intended output in assets/js/, retaining the relative path of the file
  • Finally, we construct an array of rollup config objects. The final output is a series of minified bundles (source map included) for each detected script file.

To bring this all together, let’s add some build and watch scripts to package.json to make this whole setup more dev-friendly. Note that I omitted the rest of the file, hence the ... at the beginning and end.

Now, whenever you want to build your scripts, just run npm run build.

To watch the scripts locally for development, you’ll need to have two terminal sessions active, one for npm run watch and npm run watch:scripts.

Before you commit your changes, be sure to add assets/js to your .gitignore as there’s no reason to have both the pre-processed and processed in git.

What’s Next?

If you want to take this further, there’s plenty more to explore. For example, you can create environment logic using JEKYLL_ENV and BABEL_ENV, so you can control how your scripts (and which) are bundled between local development (BABEL_ENV=development) and production (BABEL_ENV=production JEKYLL_ENV=production).

I spun up an example repo showcasing everything covered in this article. Check it out here.

I hope this was somewhat helpful. Thanks for reading!

George is a front-end developer and digital designer living in Oakland, California. By day, he’s a software engineer on ServiceNow’s design systems team. Other times, he can be found long boarding, playing video games, or collecting way too many Pokemon cards. He hates talking in the third person.

Learning to see. @gwtrev

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store