<commit_before>---
title: Creating a Blog
what: creates a simple blog
---

## Creating a simple blog with Hakyll

After we created a simple brochure site, we're going to try something more
advanced: we are going to create a simple blog system.

A [zip file] containing the source for this tutorial is also available.

[zip file]: $root/examples/simpleblog.zip

Blogs, as you probably know, are composed of posts. In Hakyll, we're going
to use simple pages for posts. All posts are located in the `posts`
directory. But we're not going to use the `directory` command here - you will
see why later. First, some trivial things like css.

~~~~~{.haskell}
main = hakyll "http://example.com" $ do
    directory css "css"
~~~~~

## Finding the posts

`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
provide us with all the blog posts. The blog posts have a
`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
can sort them easily (sorting on filename implies sorting on date). You could of
course name them whatever you want, but it's always a good idea to stick to the
conventions. They contain some metadata, too:

    title: A first post
    author: <NAME>
    date: November 5, 2009
    ---
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
    Vivamus pretium leo adipiscing lectus iaculis lobortis.
    Vivamus scelerisque velit dignissim metus...

Now, we find the posts and sort them reversed, so the most recent post will
become the first item in the list:

~~~~~{.haskell}
postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
~~~~~

Our `postPaths` value is now of the type `[FilePath]`. We want to be able to
render all posts, so we pass them to the `createPage` function.

~~~~~{.haskell}
let postPages = map createPage postPaths
~~~~~

We have two templates we want to render our posts with: first we would like to
render them using `templates/post.html`, and we want to render the result
using `templates/default.html`. This can be done with the `renderChain`
function:

~~~~~{.haskell}
mapM_ (renderChain [ "templates/post.html"
                   , "templates/default.html"
                   ]) postPages
~~~~~

Remember that the `renderChain` works by rendering the item using the first
template, creating a new page with the render result in the `$body` field, and
so on until it has been rendered with all templates.

Now, we have the posts rendered. What is left is to generate some kind of index
page with links to those posts. We want one general list showing all posts, and
we want to show a few recent posts on the index page.

## Creating listings.

`createPage` is the easiest way of reading a `Context`. But in this case, we
want something more custom, so we'll use the `createCustomPage` function. This
allows us to create a more specific `Context`.

~~~~~{.haskell}
createCustomPage :: FilePath
                 -> [(String, Either String (HakyllAction () String)]
                 -> HakyllAction () Context
~~~~~

The first argument is the `url` of the page to generate. For our index page,
this will be, `index.html`. The second argument is obviously our `key: value`<commit_msg>Fix another parentheses mismatch<commit_after>---
title: Creating a Blog
what: creates a simple blog
---

## Creating a simple blog with Hakyll

After we created a simple brochure site, we're going to try something more
advanced: we are going to create a simple blog system.

A [zip file] containing the source for this tutorial is also available.

[zip file]: $root/examples/simpleblog.zip

Blogs, as you probably know, are composed of posts. In Hakyll, we're going
to use simple pages for posts. All posts are located in the `posts`
directory. But we're not going to use the `directory` command here - you will
see why later. First, some trivial things like css.

~~~~~{.haskell}
main = hakyll "http://example.com" $ do
    directory css "css"
~~~~~

## Finding the posts

`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
provide us with all the blog posts. The blog posts have a
`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
can sort them easily (sorting on filename implies sorting on date). You could of
course name them whatever you want, but it's always a good idea to stick to the
conventions. They contain some metadata, too:

    title: A first post
    author: <NAME>
    date: November 5, 2009
    ---
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
    Vivamus pretium leo adipiscing lectus iaculis lobortis.
    Vivamus scelerisque velit dignissim metus...

Now, we find the posts and sort them reversed, so the most recent post will
become the first item in the list:

~~~~~{.haskell}
postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
~~~~~

Our `postPaths` value is now of the type `[FilePath]`. We want to be able to
render all posts, so we pass them to the `createPage` function.

~~~~~{.haskell}
let postPages = map createPage postPaths
~~~~~

We have two templates we want to render our posts with: first we would like to
render them using `templates/post.html`, and we want to render the result
using `templates/default.html`. This can be done with the `renderChain`
function:

~~~~~{.haskell}
mapM_ (renderChain [ "templates/post.html"
                   , "templates/default.html"
                   ]) postPages
~~~~~

Remember that the `renderChain` works by rendering the item using the first
template, creating a new page with the render result in the `$body` field, and
so on until it has been rendered with all templates.

Now, we have the posts rendered. What is left is to generate some kind of index
page with links to those posts. We want one general list showing all posts, and
we want to show a few recent posts on the index page.

## Creating listings.

`createPage` is the easiest way of reading a `Context`. But in this case, we
want something more custom, so we'll use the `createCustomPage` function. This
allows us to create a more specific `Context`.

~~~~~{.haskell}
createCustomPage :: FilePath
                 -> [(String, Either String (HakyllAction () String))]
                 -> HakyllAction () Context
~~~~~

The first argument is the `url` of the page to generate. For our index page,
this will be, `index.html`. The second argument is obviously our `key: value`
{'fraction_non_alphanumeric': 0.08330728352610191, 'fraction_numerical': 0.0015629884338855893, 'mean_word_length': 3.66399416909621, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6528167', 'n_tokens_mistral': 1834, 'n_tokens_neox': 1744, 'n_words': 932}