Pages and Structures

Pages and Structures are fundamental data types in Pencil. Understand them and you shall succeed.

A Page contains:

That seems easy enough. But if all we had was the Page type, Pencil wouldn’t be all that useful. It would be just a glorified Markdown renderer. To support page re-use, we need some way to combine or stitch different pages together. This is where the Structure type comes in.

A Structure defines a nesting structure of Pages. Think of them like Russian nesting dolls.

Consider the canonical Pencil example:

Here, we combine two pages, layout and index, into a single structure via layout <|| index. As described in the tutorials, underneath the hood a Structure is a non-empty linked list.

Here’s a diagram of a longer example:

render $ layout <|| a <| b <| c
Diagram describing the structure code above.
Diagram describing the structure code above.

Each page’s content is accessible to the page above through body.

So when render is called on this structure, Pencil does the following:

So how did Pencil decide on the output file path?

The Default File Path Rule. By default Pencil uses the file path of last non-collection Page in the Structure.

In this case, that would be index’s file path. When we call load "index.markdown", load looks at the original extension (.markdown) and automatically converts it to .html.

If we wanted a different output file name, we could use some of the provided methods like rename and move:

render $ layout <|| rename "another-name.html" index

If you don’t want to use the last page’s file path, you can set any preceding page as the file path by using useFilePath. For example, say the last item in your structure is a comments widget:

By default, this would render your page into the file named commen-widget.html. To use about’s file path:

render $ layout <|| useFilePath about <| comment

And now the output file path is about.html.


Sometimes we’ll want to nest not just a single page, but a collection of pages. For example, we may want to build a page listing all of the blog posts we’ve loaded. In this page, we’d want to loop through each post and render the post’s title, date and URL.

Pencil accomplishes this through collections, which is also covered in Tutorial 3: Blogging.

Consider this example:

We load two posts, and want to somehow inject them into our index page so that we can iterate through each post and print out the post’s title. To do this, we create a collection through coll "posts" posts. The first argument, "posts" is the variable name that will hold your pages. We then push this collection into the structure using (<<|).

You can now access this in index.html like this:

The Collection Rule states that a collection (1) must be the last element in the structure and (2) not the first.

So you can’t have a structure with just a collection, for example. This rule is there to keep things simple, and it makes sense too. For most use-cases, the collection is the “most important thing” for the page we’re trying to build and does not need inner-pages.

Here is a diagram describing the collection structure above:

Diagram describing a collection structure.
Diagram describing a collection structure.

But what if you need more than one collection? Say you want to list your top blog posts, and the rest of them below. You can do this “manually”:

Commonly Used Functions

Check out the Hackage docs for detailed information and examples.