Simple layouting with Gulp

Published on Last modified on

Have you ever wanted to make a really quick and simple website using Gulp with repeating components like header and footer, but didn’t quite manage to find a good layouting system? I’ve gone a bit crazy after a while of searching, so crazy that I ended up with Ember, which is not even a layouting system, it’s a MVC framework and not at all what I wanted. For Grunt there’s Assemble, but even if it existed for Gulp (it kinda does) it’s still to complicated. The solution is really dead simple—gulp-wrap.

First I thought I wanted a system where I could add partials, but what I really wanted was just a single layout file which wraps around pages. Ruby developers should be familiar with this pattern.

In my examples I’m assuming the following directory structure:

├── app
│   ├── about.html
│   ├── contact.html
│   ├── index.html
│   ├── layout.html
│   ├── scripts
│   │   └── jquery.js
│   └── styes
│       └── main.css
└── gulpfile.js

Our layout.html will basically look like this:

<!doctype html>
    <meta charset="utf-8">
    <link rel="stylesheet" href="styles/main.css">
    <nav role="navigation">
      <!-- navigation stuff -->
    <main role="main">
      <%= contents %>
    <footer role="contentinfo">
      <!-- footer stuff -->
    <script src="scripts/jquery.js"></script>

Notice the <%= contents %>, this is where our pages will be inserted. Let’s create a Gulp task for layouting (is “layouting” a real word?):

$ npm install --save-dev gulp-wrap
var wrap = require('gulp-wrap');
gulp.task('layout', function () {
  return gulp.src(['app/**/*.html', '!app/layout.html'])
    .pipe(wrap({src: 'app/layout.html'}))

Pretty simple, huh? :smiley:

I’m selecting every .html file except layout.html and inserting them into layout.html. In this case I’m writing them to dist, but you can write them wherever it’s convenient, based on your workflow. I’ll give an example…

Let’s say we’re temporarily writing these HTML files and serving them with assets as sort of a development mode. A common practice I use is to have a .tmp directory where I put all processed files, so we would need to change the destination to:


In this example I will serve the files with connect, serve-static and serve-index:

$ npm install --save-dev connect serve-static serve-index
var connect = require('connect');
var serveStatic = require('serve-static');
var serveIndex = require('serve-index');
gulp.task('connect', ['layout'], function () {
  var app = require('connect')()
    .on('listening', function () {
      console.log('Started connect web server on http://localhost:9000');

It’s very important that you serve .tmp before app, otherwise app would have precedence, which means that app/index.html, that has no layout, would get served instead of the generated .tmp/index.html.

We can also create a task that watches HTML files for changes and recompiles them:

gulp.task('watch', ['layout'], function () {'app/**/*.html', ['layout']);

Lastly we can create a default task which combines those 3 tasks:

gulp.task('default', ['layout', 'connect', 'watch']);

Because we defined that connect and watch tasks depend on layout, we’ve ensured that layout will always run first.

This is a partial example, somewhere along the way you would build this into a directory, I’ll leave it up to you :wink:


With this setup you won’t be able to do anything slightly more fancy, like changing the page title or designing the current navigation link. For that I suggest a cool templating engine I found recently, Nunjucks, which is easy to grasp simple to use.

Your layout task should instead use gulp-nunjucks-render:

$ npm install --save-dev gulp-nunjucks-render
var nunjucksRender = require('gulp-nunjucks-render');
gulp.task('layout', function () {
  return gulp.src(['app/**/*.html', '!app/layout.html'])

In your layout.html you can replace <%= contents %> with {% block content %}{% endblock %}.

Lastly, your pages should look something like this:

{% extends "layout.html" %}
{% block content %}
  <!-- page content -->
{% endblock %}

For more features, check out the docs!