Tweet

Docs /  templates

Layouts

Layouts are used for "wrapping" the content of individual pages with common elements, such as the <head></head> and footer sections, which usually contain necessities such as <link> and <script> tags.

Basic Layout

A very simple layout might look something like this:

<!DOCTYPE html> 
<html  lang ="en" > 
  <head > 
    <meta  charset ="UTF-8" > 
    <title > {{title }} </title > 
  </head > 
  <body > 
    {{> body  }} 
  </body > 
</html > 

Where {{> body }} is the point of insertion for content from any pages that use this layout.

Defining layouts

In the Assemble task options you can either specify a general layout to be used for all pages in a project:

assemble: {
  options: {
    layout: 'layouts/default.hbs' 
  }
}

Multiple layouts

When your project has different types or groupings of pages, such as "docs", "blog" and "examples" for instance, and there are certain design or structural elements that are unique to each grouping, it often makes sense to:

  1. Create a different targets in the assemble task to accomodate each grouping,
  2. Give each target (e.g. each grouping of pages) its own special layout containing the unique elements required by each grouping. For example, every page in the blog target might have a right sidebar, whereas every page in the docs target might have a left sideabar:

For example:

assemble: {
  options: {
    layout: 'default.hbs',
    layoutdir: 'layouts' 
  },
  docs: {
    // override task-level layout 
    options: {layout: 'docs-layout.hbs' },
    files: {'docs/': ['src/docs/*.hbs' ]},
  },
  site: {
    // override task-level layout 
    options: {layout: 'site-layout.hbs' },
    files: {'site/': ['src/site/*.hbs' ]},
  }
  // ... other targets 
}

Nested layouts

Layouts can also be "stacked" so that the parent-most layouts contain the most commonly used elements while "child layouts" contain elements that are only necessary for specific pages or groupings of pages.

If, for example:

  • all pages will require the same <script></script> and <link> tags,
  • some pages in your project should be rendered with a sidebar
  • and some without a sidebar

This is a good opportunity to use nested layouts that might look something like this:

Example Layout #1: parent layout

Our parent layout will only contain the most commonly required, generalized elements. We'll call this layout parent-layout.hbs:

<!DOCTYPE html> 
<html  lang ="en" > 
  <head > 
    <meta  charset ="UTF-8" > 
    <title > {{title }} </title > 
    <link  rel ="stylesheet"  href ="main.css" > 
  </head > 
  <body > 
    {{> body  }} 
    <script  src ="main.js" >  </script > 
  </body > 
</html > 

Example Layout #2: child layout

Our "child" layout will contain additional markup for our sidebar, but it should also inherit all of the markup from the parent layout (since layouts are "flattened" before pages are rendered). For the markup to be inherited, we need to specify a parent layout for our child layout.

Specifying a layout for a layout works the exact same way as specifying a layout for any reguar page. It can be defined in the options.layout property in the Assemble task config in the Gruntfile (at the task and/or target level), or the layout can be specified in the child layout's YAML front matter. Here is an example of the latter:

child-layout.hbs

---
layout: parent-layout.hbs
---
<!-- Only the child layout should have this sidebar! -->
<div class="row">
  <div class="col-lg-3">
    <div class="sidebar">
      <a href="#">Buttons</a>
      <ul class="nav">
        <li><a href="#button-expand-left"></a>Expand Left</li>
        <li><a href="#button-expand-right"></a>Expand Right</li>
        <li><a href="#button-expand-up"></a>Expand Up</li>
        <li><a href="#button-expand-down"></a>Expand Down</li>
      </ul>
    </div>
  </div>
  <div class="col-lg-9">
    <!-- render content from pages that specify this layout -->
    {{> body }}
  </div>
</div>

You might recall that the {{> body }} tag is the "insertion point" in a layout, specifying exactly where the content from each page will be passed in. Layouts are no different. A child layout will be inserted (flattened) into the parent layout at the point of the {{> body }} tag.

Example "flattened" result

Continuing with the example from the previous section, the flattened layout for pages that use the "child" layout might look something like this during build:

<!DOCTYPE html> 
<html  lang ="en" > 
  <head > 
    <meta  charset ="UTF-8" > 
    <title > {{title }} </title > 
    <link  rel ="stylesheet"  href ="main.css" > 
  </head > 
  <body > 
    <div  class ="row" > 
      <div  class ="col-lg-3" > 
        <div  class ="sidebar" > 
          <a  href ="#" > Buttons</a > 
          <ul  class ="nav" > 
            <li > <a  href ="#button-expand-left" > </a > Expand Left</li > 
            <li > <a  href ="#button-expand-right" > </a > Expand Right</li > 
            <li > <a  href ="#button-expand-up" > </a > Expand Up</li > 
            <li > <a  href ="#button-expand-down" > </a > Expand Down</li > 
          </ul > 
        </div > 
      </div > 
      <div  class ="col-lg-9" > 
        {{> body  }} 
      </div > 
    </div > 
    <script  src ="main.js" >  </script > 
  </body > 
</html > 

Layout FAQ

  • Layouts are optional. If you don't need one, don't define one.
  • At build time, nested layouts are "flattened" before content is passed in from pages (child layouts are progressively "merged" into their respective parent layouts until there are no parent layouts left in the stack.)
  • Layouts can be defined at the task-level, target-level, in the YAML front-matter of a page, or in the data.layout property for pages defined in an options.pages collection.

Layout Specificity

As with CSS, the more specific layout wins:

Level Description
none Layouts are optional.
Task Defined at the task-level of the assemble task. Project level
Target Defined at the target-level in the assemble task. Sub-project level
Layout property (data) Defined in the data: {} object in a pages collection. Page and/or sub-page level
Layout property (YFM) Defined in the YAML front matter of a page. Page and/or sub-page level

Layout example

Layouts are optional, but the {{> body }} tag is required for content to be pulled into a layout.

<!DOCTYPE html> 
<html > 
  <head > 
    <title > {{title }} </title > 
  </head > 
  <body > 
    <!-- the body "pulls in" content from pages --> 
    {{> body  }} 
  </body > 
</html > 

Granular control

  1. Task options: Great for defining a "project-wide" default.
  2. Target options: Override the "project default" in the options for each target. Offers greater control over which "types" of pages get which layout.
  3. Page-by-page: If you require more granularity you can define a layout in the YFM of a page

Multiple layouts

Since you can create as many targets as you require, defining layouts in the Gruntfile is a great way of quickly setting up your layout "defaults". In the assemble task in your Gruntfile.js, you can define a layout at the task-level, and/or a different layout for each build target:

assemble: {
  options: {
    layout: 'layouts/default.hbs' 
  },
  site: {
    files: {
      'site/': ['templates/pages/*.hbs' ]
    }
  },
  blog: {
    options: {
      layout: 'layout/post.hbs' 
    },
    files: {
      'blog/': ['templates/posts/*.hbs' ]
    }
  },
  docs: {
    options: {
      layout: 'layouts/docs.hbs' 
    },
    files: {
      'docs/': ['templates/docs/*.hbs' ]
    }
  }
}

Page-specific Layouts

If you require a higher level of granularity than defining layouts in the Gruntfile, you may also define layouts on a page-by-page basis, thus overriding both the task-level ("global") defaults and the target-level defaults.

To do so, just add the layout to the YFM of the page like this:

---
layout: path/to/layout.hbs
---

Disabling Layouts

  • Pages: add layout: false or layout: none to the YAML front matter of any page that should build without a layout.
  • Targets: add layout: false or layout: none to the options of any target that should build pages without a layout.

Example usage

Your imagination is the only limit to what can be done with layouts, so these are just examples.

<!DOCTYPE html> 
<html > 
  <head > 
    <title > {{title }} </title > 
  </head > 
  <body > 
    <!-- HEADER --> 
    <header  class ="masthead subhead" > 
      <div  class ="container" > 
        <div  class ="row" > 
          <div  class ="col col-lg-12" > 
            <h1 > <span  class ="text-muted" > Docs /&nbsp;</span >  {{#if  section }} {{ section  }}  {{else  }} {{basename }} {{/if  }} </h1 > 
          </div > 
        </div > 
      </div > 
    </header > 
    <!-- CONTENT --> 
    <div  class ="container" > 
      {{> body  }} 
    </div > 
    <!-- FOOTER --> 
    {{> footer  }} 
  </body > 
</html > 

Nested Layouts

Layouts can be nested inside other layouts. This enables highly granular control over how pages and layouts are organized. A common strategy is to use a basic "master" or "default" layout for a site, which contains only the most common, bare necessities for a site. This default layout is then inherited by other more specialized layouts.

For example, your default layout may contain the <head></head>, footer, common link and script tags for a site:

<!DOCTYPE html> 
<html  lang ="en" > 
  <head > 
    {{> header  }} 
  </head > 
  <body > 
    {{> body  }} 
    <section  id ="footer" > 
      {{> footer  }} 
    </section > 
  </body > 
</html > 

And more specialize layouts might contain side navigation for a section of the site, like this:

---
layout: default.hbs
---
<header  class ="masthead subhead" > 
  <div  class ="container" > 
    <h1 > <span  class ="text-muted" > Docs /&nbsp;</span > {{titleize  basename }} </h1 > 
  </div > 
</header > 
<ul  class ="nav nav-pills nav-stacked" > 
  {{#each  pages }} 
  <li > <a  href ="{{relative  .. /page.dest this.dest }} " > {{data.title }} </a > </li > 
  {{/each  }} 
</ul > 
{{> body  }} 

At build time layouts are "flattened" first before pages are passed in. In other words, the above example becomes this first:

<!DOCTYPE html> 
<html  lang ="en" > 
  <head > 
    {{> header  }} 
  </head > 
  <body > 
    <header  class ="masthead subhead" > 
      <div  class ="container" > 
        <h1 > <span  class ="text-muted" > Docs /&nbsp;</span > {{titleize  basename }} </h1 > 
      </div > 
    </header > 
    <ul  class ="nav nav-pills nav-stacked" > 
      {{#each  pages }} 
      <li > <a  href ="{{relative  .. /page.dest this.dest }} " > {{data.title }} </a > </li > 
      {{/each  }} 
    </ul > 
    {{> body  }} 
    <section  id ="footer" > 
      {{> footer  }} 
    </section > 
  </body > 
</html > 

Layouts for other formats

Markdown layouts

Layouts don't have to be HTML, they can be whatever language you want them to be. If you're generating markdown pages instead of HTML, maybe for a project wiki or other markdown documentation, then you'll want to use a markdown formatted layout.

In this example, instead of adding adding the traditional head and footer we'll add some link references to make sure the same ones are used on every generated page. This makes it easier to maintain links and it also cuts down on potential for broken links:

{{> body }}
*  * * 
_This page was generated using Grunt and [assemble][repo] on {{ today }}._ 
[org]: https://github.com/assemble
[repo]: https://github.com/assemble/assemble
[issues]: https://github.com/assemble/assemble/issues
[docs]: https://github.com/assemble/assemble-docs

README Layout

# {{pkg.name}} {{travis-badge}} 
> {{pkg.description}} 
{{> body }}
*  * * 
_This readme was generated using Grunt and [assemble][repo] on {{ today }}._ 
[org]: https://github.com/assemble
[repo]: https://github.com/assemble/assemble
[issues]: https://github.com/assemble/assemble/issues
[docs]: https://github.com/assemble/assemble-docs

Layout FAQ

  • Layouts are optional
  • When a layout is specified, it must include a {{> body }} tag to render content from any file that uses the layout.
  • Layouts may be defined in the Gruntfile or in YFM of a page.
  • A layout defined for a target will override a layout defined at the task level.
  • A layout defined in YFM will override a layout defined in the Gruntfile
  • Lo-dash templates can be used in YAML front-matter to specificy a layout.

See the template for this page →

Find an error? Let us know →