ページを量産したりチームでサイトを作る時pugという言語を使ってWebサイトを作っています。pug(旧:Jade)の特徴やメリット、基本的な使い方、導入方法(pug-cliとgulp)についてまとめました。とくに記述方法はif(分岐)やfor・each(ループ処理)など解説。テンプレート化したい場合に使う記述方法の解説と具体的なテンプレートの作り方なども紹介しています。
普段webpackやgulpを使っている方なら導入 のハードルも低いと思います。
今回はmpmコマンド出始めたい方はpackage.jsonで手軽にすぐ始められます!とても記事は長いので目次を利用して好きなところを読んでください。

かみーゆ/フロントエンドエンジニア
pugとは?
pugとは、Haml(HTMLを抽象化したマークアップ言語)記法に影響を受けたJavaScriptで実装された高機能テンプレートエンジンです。
より短く、キレイかつ簡潔にコードを書くことができます。
The general rendering process of pug is simple. pug.compile() will compile the pug source code into a JavaScript function that takes a data object (called “locals”) as an argument. Call that resultant function with your data, and voilà!, it will return a string of HTML rendered with your data.
The compiled function can be re-used, and called with different sets of data.
公式サイト:pug
昔は、*Jade(ヒスイ)*と呼ばれていましたが、すでに商標登録されていたのでpugに名前が変更されました(Version 2以降)。
似たようなテンプレートエンジンでEJSもあります。参考にしてください。
私の考えるpugでコーディングするデメリットとメリットを紹介します。
導入のメリット
- 大量のページを手分けして量産できる
- 短いコードでキレイかつ簡潔にコードが書ける
- ヘッダーや共通部分とそれ以外を分けて書ける

こちらは私が作っているサイトの実際のpugファイルです。footerやheaderなどを分けてデフォルトで読み込み、主要コンテンツだけ毎度変更しています。
導入のデメリット
- 実務で使うためにはgulpやwebpackのタスクを書いたりコマンドを覚える必要がある
- インデントをミスると意図しないコンパイルが起きる
- コードの書き方など多少の学習コストがかかる

こちらは私が実際に利用しているLaravel mixのwebpackのコードです。タスクを書いたりしないといけないのは少し面倒ですね。
導入方法
pugはnpm経由でインストール可能です。
npmのモジュールを管理できるようにpackage.jsonを作っておきます。
今回はpug-cli(npmスクリプト)とgulpでの導入方法をご紹介します。
メリット | デメリット | |
---|---|---|
gulp | SCSSなどとあわせて複雑なタスクが書ける | モジュールを複数インストールしなければならないことがある。複雑なコードを書く場合がある |
pug-cli | 記述量が少なくてすむ。 | 複雑なタスクには不向き |

HTMLをコンパイルするだけなら pug-cli で十分です。
package.jsonを作成し、npmモジュールを管理しながら、進めていきます。
今回は pug-practice というディレクトリを作り、ディレクトリに移動して以下コマンドを実行します。VS Codeであればディレクトリを開いて、ターミナルを開いて実行すればOKです。
control + shift + @
で開くことができます。npm init
# OR
npm init -y
通常プロジェクト名など、対話式で入力し作成していくのですが-y
のオプションを使うと面倒な入力を省くことができます。
{
"name": "pug-practice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"pug": "pug ./src --hierarchy -o ./dist -P"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
}
}
package.json ファイルが作成できました。
pug-cliで始める

次にpugをインストールします。インストールでコケる場合はsudo(管理者権限で実行)をつけてみてください。
npm install pug pug-cli -D
pug
とpug-cli
がインストールされます。
"pug": "^3.0.2"
"pug-cli": "^1.0.0-alpha6"
{
"name": "pug-practice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"pug": "^3.0.2",
"pug-cli": "^1.0.0-alpha6"
}
}
src
と dist
ディレクトリを作成し、空の index.pug
を追加しておきます。
pug-practice/
├ node_modules/
├ package.json
├ src/
| └ index.pug(追加)
└ dist/
src
ディレクトリにある index.pug
を index.html
として dist
ディレクトリに出力します。
pug ./src --hierarchy -o ./dist -P -w
毎度、コマンドを打つのは面倒なので、scriptとして登録しておきます。
{
"name": "pug-practice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"pug-w": "pug ./src --hierarchy -o ./dist -P -w"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"pug": "^3.0.2",
"pug-cli": "^1.0.0-alpha6"
}
}
すると以下コマンドで実行できるようになります。
npm run pug-w

src
ディレクトリ内のpugファイルを更新するとsrc
ディレクトリ内にHTMLファイルがコンパイルされます。-w
オプションが付いているのでリレンダー(再コンパイル)されます。
※scriptのオプションは記事の最後にあります。
Macであれば Control+C
で実行を抜けることができます。
Gulpで始める

gulp
と gulp-pug
をインストールします。ディレクトリを作成し npm init -y
などで packge.json ファイルを作成した状態からスタートします。pug-cli
と同じディレクトリ名を使っていますが、別々に試したい場合は名前を変えてください。
ライブリロードさせたいので browser-sync
エラーを吐いてもgulpを止めないためにgulp-plumber
をあわせてインストールします。
npm install gulp-pug gulp browser-sync gulp-plumber -D
{
"name": "pug-practice",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"browser-sync": "^2.27.10",
"gulp-plumber": "^1.2.1",
"gulp": "^4.0.2",
"gulp-pug": "^5.0.0"
}
}
src
と dist
ディレクトリを作成し、空の index.pug
を追加しておきます。タスクを記述する gulpfile.js を追加します。
pug-practice/
├ node_modules/
├ package.json
├ gulpfile.js(追加)
├ src/
| └ index.pug(追加)
└ dist/
タスクを以下の通り書きます。
const { src, dest, series, parallel, watch } = require("gulp");
const pug = require('gulp-pug');
const plumber = require("gulp-plumber");
const bs= require("browser-sync");
// browserSync
function bsInit(done) {
bs.init({
server: {
baseDir: "./dist/",
},
reloadDelay: 1000,
open: false,
});
done();
}
// pug
function html(done) {
src('./src/*.pug')
.pipe(plumber())
.pipe(pug({pretty: true}))
.pipe(dest('./dist'))
.pipe(bs.stream())
done();
}
// watch
function watchTask(done) {
watch('./src/*.pug', html);
done();
}
exports.default = series(parallel(bsInit,html),watchTask);
npmスクリプトを変更します。
"scripts": {
"start": "gulp"
},
npm start

基本のコードのご紹介
基本のpugの記述方法です。
基本中の基本・単純なタグと文字列を出力したい場合
pugの記法はタグ名+スペース+コンテンツです。閉じタグは不要です。
p hello,pug!
これでファイルはコンパイルできましたね!!
<p>hello,pug!</p>
htmlを直に書くことができます。
div
<p>hello,pug!</p>
<div><p>hello,pug!</p></div>
=(イコール) でタグ内にコンテンツを内包させることもできます。
p='こんにちは。IT戦士のかみーゆです。'
<p>こんにちは。IT戦士のかみーゆです。</p>
エスケープ処理
出力内容をエスケープしたくないことがありますよね? そんなときは!(エクスクラメーション・マーク)を使うとそのまま出力されます。
p='見出し1は<h1>タグで囲みます'
p!='見出し1は<h1>タグで囲みます'
<p>見出し1は<h1>で囲みます。</p>
<p>見出し1は<h1>で囲みます。</p>
コメント
コメントをpug上には残して、htmlには反映したくない場合は */(スラッシュ)2つ+-(ハイフン)*を文前に追加。
//- ここにコメントをかく
htmlのコメントとして残したい場合は /(スラッシュ)2つです。
// ここにコメントをかく
入れ子(ネスト)構造を書きたい場合
入れ子にしたい場合はインデント(字下げ)を使います。 以下のような書き方をします。
header
h1 hello, pug!
<header>
<h1>hello, pug!</h1>
</header>
もしくは*:(コロン)+スペース*を使います。
div: p test
<div>
<p>test</p>
</div>
idとclassの出力
idは#
、classは.
でつなぐ。
section#hoge
div.piyo
<section id="hoge"></section>
<div class="piyo"></div>
またidやclassを使うとき、divは省略できます。
.piyo
srcやhlefなどの属性の出力方法
属性は()内に属性名=値で記載できます。複数あるときは、,(カンマ)かスペースで区切ります。
img(src="image.jpg", alt="画像", width="300", height="200")
a(href="./" target="_blank") トップページ
<img src="image.jpg" alt="画像" width="300" height="200"/>
<a href="./" target="_blank">トップページ</a>
id、クラスも同様にカッコ内にも書けます。
section(id="hoge")
div(class="piyo")
マルチラインで書くこともできます。
input(
type='checkbox'
name='agreement'
checked
)
<input type="checkbox" name="agreement" checked="checked"/>
`(バッククォート) を使えば、属性の値を複数行にまたいで書くこともできる。
input(data-json=`
{
"very-long": "piece of ",
"data": true
}
`)
<input data-json="
{
"very-long": "piece of ",
"data": true
}
" />
さらに&attributes()
を使って、後から属性を連想配列として追加できます。
a(href="/")&attributes({'style':'color:white','target':'_blank'}) リンク
<a href="/" style="color:white;" target="_blank">リンク</a>
改行追加
インライン扱いされるようなタグは改行されずコンパイル時美しく整形されません。 改行を加えたい場合は |(パイプ)2つを使います。
a(href="/") HOME
|
|
a(href="/blog") Blog
<a href="/">HOME</a>
<a href="/blog">Blog</a>
文字列の中にタグ
文字列の中に文字を入れる方法が2つあります。 1つは *|(パイプ)*を使う方法です。
p
| これは
strong テキスト
| です
もう一つは#[]
で囲む方法です。
p これは#[strong テキスト]です
前述したものに比べてこちらの方がはるかにカンタンなのでよく使います。
<p>これは<strong>テキスト</strong>です</p>