Jekyll Static Site Generation with Vue.js Single File Components
April 04, 2018
Jekyll
Static Site Generators (SSG) are awesome and Jekyll is one of the most robust options available. It’s stable, well supported and straightforward. What would make it even better? The reactivity of Vue.js Single File Components (SFC) and webpack optimizations, of course.
In this article we’ll combine the configurability of Jekyll SSG with the reactivity of Vue. We’ll cover starting a new Jekyll project, installing Vue, configuring webpack to compile SFCs, and finally mount SFCs in Jekyll views ⚡.
This tutorial will assume a basic knowledge of Jekyll and Vue.js.
Setting Up the Project
We’ll begin by scaffolding a new Jekyll project. If you don’t have Jekyll installed, check out the official installation guide.
New Jekyll Project
jekyll new jekyll-vue-template
We’ll disable the default theme, Minima, that Jekyll ships with by commenting it out of or deleting it from our config.yml
file.
config.yml
# theme: minima
Next, we’ll create a _layouts
directory to store our page layouts and create a default layout, default.html
, within it. In our layout we’ll create a top-level <div>
, which will be used later as an entrypoint for Vue.
default.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Example Title</title>
</head>
<body>
<div id="app">
{{ "{{ content " }}}}
</div>
</body>
</html>
Lastly, we’ll change the layout type in the YAML front matter of index.md
at the project root to match our newly created default
layout:
index.md
---
layout: default
---
# jekyll-vue-template
Let’s test to ensure that our Jekyll project scaffolding was successful by spooling up a dev server using the following command:
bundle exec jekyll s
npm Initialization
After we’ve successfully setup Jekyll, we’ll initialize an npm workflow in the project root allowing us to work with node modules, including Vue.js and webpack.
npm init
Be sure to update the
exclude
field in yourconfig.yml
file to keep various development environment files, such aspackage.json
, from being generated into your Jekyll production build.
Installing Vue & Configuring webpack
Ok, take a deep breath. It’s time to take a dive into installing and configuring webpack from scratch. This can be a daunting process, but we’ll go through this process step-by-step. If you’re comfortable with configuring webpack with Vue, feel free to skip to the Integrating Vue and Jekyll section.
Installing Vue
First, we’ll install Vue as a dependency:
npm i -S vue
Next, we’ll create a directory, src
, in our project root where all of our Vue application components will live. We’ll also create an entry point for our Vue application, main.js
. For now, we’ll just import Vue.
main.js
import Vue from 'vue'
Installing webpack and its Modules
Next, we’ll install the webpack
core module and webpack-cli
, which allows us to pass commands and arguments through the CLI to webpack.
npm i -D webpack webpack-cli
We’ll also install several modules and loaders that will assist in handling assets for webpack processing. Let’s go through the purpose of each module:
cross-env
smooths out the inconsistencies of using environment variables across platformscss-loader
allows webpack to resolve CSS@import
andurl()
statementsnode-sass
is required for compiling SASS/SCSSsass-loader
allows webpack to compile SASS/SCSS to CSSvue-loader
allows webpack to compile Vue Single File Components into JS modulesvue-style-loader
allows webpack to dynamically inject CSS into the DOMvue-template-compiler
used byvue-loader
to precompile Vue templates to render functions
npm i -D cross-env css-loader node-sass sass-loader vue-loader vue-style-loader vue-template-compiler
Configuring webpack
We’ll begin configuring our webpack workflow by creating a config file, webpack.config.js
in the project root. We’ll set the entry point to the main.js
file that we created earlier and set the output to dist/build.js
(both the directory and the bundled script file will be created when we run webpack). We’ll set the default environment mode
to development
.
webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'build.js',
},
mode: 'development',
}
Note: webpack 4 now requires the environment
mode
to be explicitly set. We set it todevelopment
by default in our config file and will programmitically set it with npm scripts later in the tutorial.
Next, we’ll add a module rule for Vue SFCs. This rule uses a regular expression to parse any file with a .vue
extension and loads it with vue-loader
. We’ll also pass in several loaders as options so that our SFC styles can be resolved.
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
scss: ['vue-style-loader', 'css-loader', 'sass-loader'],
},
},
},
]
}
The Vue package ships with several library versions. To make sure we use the correct one, we’ll define an alias for vue
, which webpack will resolve to the runtime + compiler (vue.esm.js
) version of Vue. For more information on the Vue runtime and compiler, visit the official Vue docs.
To make working with Vue components and other modules more convenient, we’ll add an array of file extensions for webpack to resolve when using import statements. This allows us to call a module import using only the filename without the extension. For example, we can use import vueComponent from 'components/vueComponent'
without the .vue
extension.
webpack.config.js
resolve: {
alias: {
vue$: 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
}
Lastly, we’ll perform a check on our environment variable to control build settings. If the environment variable is set to production
, we’ll overwrite the default development
mode that we defined earlier. Webpack will perform several useful optimizations if the mode is set to production
.
webpack.config.js
if (process.env.NODE_ENV === 'production') {
module.exports.mode = 'production'
}
Putting it All Together
webpack.config.js
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'build.js',
},
mode: 'development',
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
scss: ['vue-style-loader', 'css-loader', 'sass-loader'],
},
},
},
],
},
resolve: {
alias: {
vue$: 'vue/dist/vue.esm.js',
},
extensions: ['*', '.js', '.vue', '.json'],
},
}
if (process.env.NODE_ENV === 'production') {
module.exports.mode = 'production'
}
Once we’ve configured webpack, we’ll create the scripts needed to run it. We’ll use three separate scripts, dev
for bundling unminified assets, build
for bundling production-ready assets, and watch
for automatic recompilation of assets during development.
package.json
"scripts": {
"dev": "cross-env NODE_ENV=development webpack",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
"watch": "cross-env NODE_ENV=development webpack --watch"
}
Sanity Check - Testing our Current Progress
Now that we have Jekyll running and Vue with webpack configured, it’s time to test that our workflow is working as intended. We’ll test our webpack build by running our dev
script:
npm run dev
We should see no errors during the build process and we should now have a newly created dist
directory in the project root containing a build.js
file. If we take a look at build.js
, we’ll see that it includes the Vue.js module. You can further test the workflow by running the build
and watch
scripts as well.
Note that when running
npm run build
, ourbuild.js
bundle is minified. webpack 4 now performs all sorts of optimizations, such as minificiation, by default when the environment mode is set toproduction
.
Integrating Vue and Jekyll
Creating a Simple Vue Component
In our src
directory, we’ll create a components
directory to house all of our application components. Within this folder, we’ll create a simple HelloWorld.vue
single file component and give it a basic SFC scaffolding. We’ll also add in an <h1>
title, a component name, and a few styles:
HelloWorld.vue
<template>
<h1>Jekyll-Vue Template</h1>
</template>
<script>
export default {
name: 'HelloWorld',
}
</script>
<style scoped>
h1 {
color: darkslategrey;
font-size: 2rem;
}
</style>
Register Vue Components
Now that we’ve created a Vue component, we’ll need to register it with our primary Vue instance. We’ll do this by importing the component in our main.js
file. We also need to create a new Vue instance and give it an element entry point. We’ll use an id of app
and update our Jekyll layout to reflect this shortly. Lastly, we’ll attach the component to the Vue instance using the component
method.
main.js
import Vue from 'vue'
import HelloWorld from './components/HelloWorld'
Vue.component('helloWorld', HelloWorld)
const app = new Vue({
el: '#app',
})
Mounting Vue Components in Jekyll Pages
Let’s update our default layout, default.html
, with the id of app
(we defined this in main.js
). With that in place, we can now mount our component using <hello-world>
. We can mount instances of this component in any view that extends the default layout, including markdown posts, HTML includes, and HTML layout files.
default.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Example Title</title>
</head>
<body>
<div id="app">
<hello-world></hello-world>
{{ "{{ content " }}}}
</div>
</body>
</html>
or…
index.md
---
layout: default
---
<hell-world></hello-world>
Wrapping Up
Static Site Generators like Jekyll offer some unique benefits over Single-Page Applications, especially for sites with lots of static content (like blogs). They’re fast, SEO-friendly, and offer site-wide variables, like post collections. Incorporating a reactive library like Vue.js can help to make a statically-generated site feel much more dynamic.
jekyll-vue-template
If you’d like to review the source for an example Jekyll-Vue project like the one that we’ve built in this article, check out the jekyll-vue-template. It’s a boilerplate Jekyll project with support for Vue single file components complete with webpack 4 optimizations, including cache-control, code-splitting, and Babel transpiling, and linting with ESLint.