5 Steps To Add Bootstrap 4 To Jekyll The Right Way

Last modified:


One of the keys to use Bootstrap successfully is to be able to use and redefine its variables in our custom designs. We should not simply add Bootstrap’s javascript and CSS stylesheets to use its components, we need to change them and not making all the web look boringly the same.

This is a guide to make it easy to use Bootstrap 4 with a Jekyll website and be able to use and customize its variables as well as defining new ones.


Jekyll provides built-in support for syntactically awesome stylesheets (Sass).

Sass is a CSS extension language, it provides:

  • Variables
  • Nesting elements
    • Loops
    • Arguments
  • Selector inheritance

It consists of two syntaxes:

  • the original, indented, syntax uses the .sass extension.
  • the newer syntax, more similar to CSS, uses .scss extension.

To make Jekyll process these SASS files, we need to create files with the proper extension name (.scss or .sass) and start the file contents with two lines of triple dashes.

A file named assets/main.scss will be rendered like assets/main.css.

As Bootstrap switched from Less to Sass1 now we can use it directly without relying in parallel projects like bootstrap-sass needed for Bootstrap 3.

Installing Bootstrap 4

We will be using the package manager npm to install Bootstrap and a fresh Jekyll installation without any theme.

First we create an empty Jekyll instance and initialize npm installing Bootstrap and its dependencies.

$ jekyll new mysite --blank
$ cd mysite
$ npm init --force
Wrote to /tmp/mysite/package.json:

  "name": "mysite",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  "keywords": [],
  "author": "",
  "license": "ISC"

$ npm install bootstrap
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN bootstrap@4.1.3 requires a peer of jquery@1.9.1 - 3 but none is installed. You must install peer dependencies yourself.
npm WARN bootstrap@4.1.3 requires a peer of popper.js@^1.14.3 but none is installed. You must install peer dependencies yourself.
npm WARN mysite@1.0.0 No description
npm WARN mysite@1.0.0 No repository field.

+ bootstrap@4.1.3
added 1 package in 6.096s

$ npm install jquery
npm WARN bootstrap@4.1.3 requires a peer of popper.js@^1.14.3 but none is installed. You must install peer dependencies yourself.
npm WARN mysite@1.0.0 No description
npm WARN mysite@1.0.0 No repository field.

+ jquery@3.3.1
added 1 package in 1.712s

$ npm install popper.js
npm WARN mysite@1.0.0 No description
npm WARN mysite@1.0.0 No repository field.

+ popper.js@1.14.4
added 1 package in 1.455s

So far, we have added this structure (excluding jekyll files):

$ tree -L 2
β”œβ”€β”€ node_modules
β”‚Β Β  β”œβ”€β”€ bootstrap
β”‚Β Β  β”œβ”€β”€ jquery
β”‚Β Β  └── popper.js
β”œβ”€β”€ package.json
└── package-lock.json

4 directories, 2 files

If you aren’t working in a Jekyll project, you can add it now to the current directory with jekyll new . –force.

Adding Sass load paths

We need to tell Jekyll the path of our Sass files.

Jekyll will look at the folder specified by the sass_dir configuration key (/_sass by default), but it also supports extending it, so it will process other folders too.

Instead of setting a custom sass folder with:

    sass_dir: _sass

we use the load-paths2 key in _config.yml to include the node_modules directory:

        - _sass
        - node_modules
load_paths only works when not in safe mode[^safe-mode] (i.e. it won't work natively with Github Pages.), if you use Github Pages you need to process the site locally and upload the resulting site with a `.nojekyll`.

Whitelist node_modules

Jekyll 3.3 started excluding the node_modules directory by default, so we have to make sure it is included in the final site to be able to use its files:

exclude: []

Add javascript

Add Bootstrap JavaScript at the end of the document so the pages load faster, just before the </body> HTML tag.

We add them in the default layout at _layouts/default.html or in footer.html in the _includes folder:

	<script src="{{'/node_modules/jquery/dist/jquery.min.js' | prepend: site.baseurl}}"></script>
	<script src="{{'/node_modules/popper.js/dist/umd/popper.min.js' | prepend: site.baseurl}}"></script>
	<script src="{{'/node_modules/bootstrap/dist/js/bootstrap.min.js' | prepend: site.baseurl}}"></script>

Import Bootstrap and use Sass variables

Create variables Sass partial

To be able to define new variables and reuse the ones defined in Bootstrap, we create a new partial Sass file _sass/_variables.scss:

  1. define our variables
  2. “Overwrite” the ones we want from Bootstrap node_modules/bootstrap/scss/_variables.scss before loading them and then
  3. import the Bootstrap variables.
All the variables defined in Bootstrap 4 have the !default**[^scss_default] property at the end. When Jekyll process each Scss file, it only defines the variables that do not have been assigned any value yet, so we can define Bootstrap's variables before Bootstrap itself define them. It is important to do this before importing the variables because there are many of them depending on each other to calculate CSS properties values.

In _sass/_variables.scss:

$white:    #fff;
$red:     #dc3545;
$body-bg: $red;
$body-color: $white;

/* custom vars */
$custom-font-size: 20px;

Import variables from main Sass file

After we have our variables, we import them from our main style sheet:

In assets/main.scss we import them and then work with our styles, using the above variables (all Bootstrap variables and mixins are available):


@import "variables";
@import "bootstrap/scss/bootstrap";

.content {
  font-size: $custom-font-size;
Don't miss the triple dashes at the beginning of the file to ensure Jekyll reads the file to be transformed into CSS later

Add css to layout

After we have our assets/main.scss, Jekyll will process it and generate the final CSS file: assets/main.css.

That is the path we need to add to our layout, in the <head> section of _layouts/default.html we include the css: <link rel="stylesheet" href="/assets/main.css">

<!-- site css -->
<link rel="stylesheet" href="{{'/assets/main.css' | prepend: site.baseurl}}">

All together

This is the basic flow Jekyll follows processing these Scss files to generate assets/main.css:

graph TB STYLESSCSS["Jekyll reads /assets/main.scss"]-->partials{"imports Sass partials"} partials--> |"import variables"|VARS partials--> |"import bootstrap/scss/bootstrap"|BS_SCSS VARS["/_sass/_variables.scss"]-->STYLESCSS BS_SCSS["/node_modules/bootstrap/scss/bootstrap.scss"]-->STYLESCSS STYLESCSS["Generates /assets/main.css"]

Now we have:

$ jekyll build
Configuration file: /tmp/mysite/_config.yml
            Source: /tmp/mysite
       Destination: /tmp/mysite/_site
 Incremental build: disabled. Enable with --incremental
                    done in 9.795 seconds.
 Auto-regeneration: disabled. Use --watch to enable.



You’ll find a minimalistic example site hosted on Github showing the result of following this tutorial at https://github.com/marcanuy/jekyll-bootstrap4).


It is also part of a Jekyll starter site jekyll-skeleton.

OPTIONAL: Keep node_modules out of _site

Currently, all node_modules packages are being copied to the final site, to keep them out we will copy the files that we are including directly from the node_modules directory to a new one containing just these files in each build, that means, we have to set up a script replacement for jekyll build and jekyll serve.

In this case I will be using the classic make program, each task is pretty self explanatory and can also be ported easily to Grunt or any other task automation solution.


Create a file called Makefile at root level with this content:

SHELL := /bin/bash
NPM := npm
VENDOR_DIR = assets/vendor/
JEKYLL := jekyll

PROJECT_DEPS := package.json

.PHONY: all clean install update

all : serve

	$(JEKYLL) doctor
	$(HTMLPROOF) --check-html \
		--http-status-ignore 999 \
		--internal-domains localhost:4000 \
		--assume-extension \

install: $(PROJECT_DEPS)
	$(NPM) install

update: $(PROJECT_DEPS)
	$(NPM) update

	mkdir -p $(VENDOR_DIR)
	cp node_modules/jquery/dist/jquery.min.js $(VENDOR_DIR)
	cp node_modules/popper.js/dist/umd/popper.min.js $(VENDOR_DIR)
	cp node_modules/bootstrap/dist/js/bootstrap.min.js $(VENDOR_DIR)

build: include-npm-deps
	$(JEKYLL) build

serve: include-npm-deps
	JEKYLL_ENV=production $(JEKYLL) serve

It is just a wrapper of Jekyll build and install commands handling dependencies.

Remember that those spaces are TAB's or make will fail.

Now we will use make build and make serve to work with Jekyll as wrappers of jekyll build and jekyll serve respectively.

Using new paths

It just remain to update our paths in the layout, in default.html use them as:

<script src="{{'/assets/vendor/jquery.min.js' | prepend: site.baseurl}}"></script>
<script src="{{'/assets/vendor/popper.min.js' | prepend: site.baseurl}}"></script>
<script src="{{'/assets/vendor/bootstrap.min.js' | prepend: site.baseurl}}"></script>

Finally, tell Jekyll not to include the node_modules directory in the final site, in _config.yml:

 - node_modules
This is the default behaviour from Jekyll 3.3


Now we are just including in our website the files we chose from the node_modules folder, placing them in assets/vendor and avoiding to have any other unnecessary files in the final website.


When we build our site, Jekyll will process the .scss files with our custom variables and we will have them in our _site/assets/main.css. In this example its content starts with the Bootstrap code and ending with our custom _main.scss processed, looking like:

 * Bootstrap v4.0.0 (https://getbootstrap.com)
 * Copyright 2011-2018 The Bootstrap Authors
 * Copyright 2011-2018 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
:root {
  --blue: #007bff;
  --indigo: #6610f2;
  --purple: #6f42c1;

/* rest of bootstrap code... */

/* custom style */
  font-size: 20px; 

You can watch a video implementing the above code at:

This article full code is avaiable at https://github.com/marcanuy/jekyll-bootstrap4 .


  • 2018-08-30:
    • using npm instead of yarn
    • using Makefile to copy assets to final site, Jekyll now keeps node_modules out of final site by default


*[CSS]: Cascading Style Sheets

  1. http://blog.getbootstrap.com/2015/08/19/bootstrap-4-alpha/

    Moved from Less to Sass. Bootstrap now compiles faster than ever thanks to Libsass, and we join an increasingly large community of Sass developers.

  2. Issue referring the code at https://github.com/jekyll/jekyll-sass-converter/blob/master/lib/jekyll/converters/scss.rb#L77 ↩︎

Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus

Guide to add Bootstrap 4 to Jekyll with focus on having also a CSS stylesheet using its own variables and custom ones.

Clutter-free software concepts.
Translations English EspaΓ±ol

Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.

Powered by SimpleIT Hugo Theme