---
author: "Robert Pearce"
authorTwitter: "@RobertWPearce"
desc: "Use the hakyll static site generator to set up a website or blog."
keywords: "hakyll, hakyll tutorial, hakyll blog, hakyll blog tutorial, hakyll static site, static site generator"
title: "Hakyll Pt. 1 – Setup & Initial Customization"
updated: "2023-02-11T16:00:00Z"
---

This is part 1 of a multipart series where we will look at getting a website /
blog set up with [hakyll](https://jaspervdj.be/hakyll) and customized a fair
bit.

* Pt. 1 – Setup & Initial Customization
* [Pt. 2 – Generating a Sitemap XML File](/hakyll-pt-2-generating-a-sitemap-xml-file.html)
* [Pt. 3 – Generating RSS and Atom XML Feeds](/hakyll-pt-3-generating-rss-and-atom-xml-feeds.html)
* [Pt. 4 – Copying Static Files For Your Build](/hakyll-pt-4-copying-static-files-for-your-build.html)
* [Pt. 5 – Generating Custom Post Filenames From a Title Slug](/hakyll-pt-5-generating-custom-post-filenames-from-a-title-slug.html)
* [Pt. 6 – Pure Builds With Nix](/hakyll-pt-6-pure-builds-with-nix.html)
* [The hakyll-nix-template Tutorial](/the-hakyll-nix-template-tutorial.html)

## Overview

1. [Installation & Setup](#installation-setup)
1. [Configuration Rules](#configuration-rules)
1. [Blog Posts](#blog-posts)
1. [Working With Templates](#working-with-templates)
1. [Rendering Partials](#rendering-partials)

## Installation & Setup

While this is detailed fully on [the hakyll installation
tutorial](https://jaspervdj.be/hakyll/tutorials/01-installation.html), I will
repeat it here.

1. install [stack](https://docs.haskellstack.org/en/stable/README/) and make
   sure `$HOME/.local/bin` is included in your `PATH`
1. `λ stack install hakyll` – should install `hakyll-init` in `$HOME/.local/bin`
1. `λ hakyll-init ourblog.com`
1. `λ cd ourblog.com`
1. `λ stack init`
1. `λ stack build`
1. `λ stack exec site build`
1. `λ stack exec site rebuild` – to test the rebuild command
1. `λ stack exec site watch` – starts dev server & watches for changes
1. navigate to http://localhost:8000 to see it!

## Configuration Rules
Hakyll gives you the ability to override its [existing configuration
rules](https://jaspervdj.be/hakyll/reference/Hakyll-Core-Configuration.html) to
change anything from the output directory (default `_site/`) to deploy commands
to the host and port for previewing your site locally.

Here is what the default configuration looks like in hakyll ([source](https://github.com/jaspervdj/hakyll/blob/1abdeee743d65d96c6f469213ca6e7ea823340a7/lib/Hakyll/Core/Configuration.hs#L88-L110)):

```haskell
-- | Default configuration for a hakyll application
defaultConfiguration :: Configuration
defaultConfiguration = Configuration
    { destinationDirectory = "_site"
    , storeDirectory       = "_cache"
    , tmpDirectory         = "_cache/tmp"
    , providerDirectory    = "."
    , ignoreFile           = ignoreFile'
    , deployCommand        = "echo 'No deploy command specified' && exit 1"
    , deploySite           = system . deployCommand
    , inMemoryCache        = True
    , previewHost          = "127.0.0.1"
    , previewPort          = 8000
    }
  where
    ignoreFile' path
        | "."    `isPrefixOf` fileName = True
        | "#"    `isPrefixOf` fileName = True
        | "~"    `isSuffixOf` fileName = True
        | ".swp" `isSuffixOf` fileName = True
        | otherwise                    = False
      where
          fileName = takeFileName path
```

The [hakyll tutorial on rules, routes and
compilers](https://jaspervdj.be/hakyll/tutorials/03-rules-routes-compilers.html)
makes reference to a `hakyllWith` function for customizing configuration, so
let's see how we can use that.

The default hakyll `main` function in your `site.hs` file looks like this:

```haskell
main :: IO ()
main = hakyll $ do
```

What we can do is change `hakyll` to `hakyllWith` and pass a function that
we'll name `config` that makes use of the `defaultConfiguration` but returns a
new, altered [record](https://en.wikibooks.org/wiki/Haskell/More_on_datatypes#Named_Fields_.28Record_Syntax.29):

```haskell
main :: IO ()
main = hakyllWith config $ do
  -- ...

config :: Configuration
config = defaultConfiguration
    { destinationDirectory = "docs"
    , previewPort          = 5000
    }
```

Whenever we make a change to `site.hs`, we need to make sure we use `stack` to
`build` it again and restart our server. We'll also need to make sure we clean
out our old output folder with the `clean` command. So, all together now:

```
λ stack exec site clean
λ stack build
λ stack exec site watch
```

...and now your output will be in the `docs/` folder, and your site will be
previewable at http://localhost:5000.

Now that we've flexed our configuration muscles a bit, let's look at the
`posts/` folder to see what we're working with on the blog side.

## Blog Posts

If you open the `posts/` folder and select any preset blog post (hint: you can
see them online at
https://github.com/jaspervdj/hakyll/tree/master/data/example/posts; make sure
you click the "Raw" button to view the raw markdown), you'll see a standard
markdown file containing two sets of content:
* metadata (between the `---` delimiters)
* body content (everything else)

From http://localhost:5000, let's click on the first post we see:
http://localhost:5000/posts/2015-12-07-tu-quoque.html. If we open up the
corresponding file, `2015-12-07-tu-quoque.html`, in our text editor, we can see
there are two metadata fields: `title` and `author`. Let's change them:

```md
---
title: Some Latin Text
author: Some Roman Person
---
```

Refresh the page and see the changes!

But note that despite changing the title of your blog post, the outputted HTML
file is still located at http://localhost:5000/posts/2015-12-07-tu-quoque.html.
This is because the _markdown filename_ is what currently determines the
outputted filename. We will change this in Part 5 of this series, but until
then, if you change the title of your post, it would be a good idea to also
change the filename.

Feel free to edit these metadata fields and markdown content with your own blog
post material.

Next up, we'll see about how we can customize the templates to work with
all the metadata that we might want to include from our posts (description,
author, keywords, image, etc).

## Working With Templates

There is a [hakyll turorial on templates, context and control
flow](https://jaspervdj.be/hakyll/tutorials/04-compilers.html#templates) that
you should check out. Here, we're going to adjust the default templates to suit
our needs.

The HTML templates can be found in – you guessed it – the `templates/` folder.

The first file we will look at is `templates/default.html` (hint: this template
is also viewable online at
https://github.com/jaspervdj/hakyll/blob/master/data/example/templates/default.html).

Templates are nothing more than `.html` files but with a caveat (which you'd
know about if you read the tutorial above): there is added context – drawn from
markdown options or injected before compilation in `site.hs` – that can be
used anywhere, so long as it is between `$` (dollar signs). Here is an example
that uses the `title` property that is set in each file:

```html
<title>$title$</title>
```

Cool! Now what if we wanted to use our `author` metadata?

```html
<meta name="author" content="$author$">
```

Oh no!

```text
Compiling
  updated templates/default.html
  [ERROR] Missing field $author$ in context for item about.rst
```

This is because not all of our files being run through this default template
have all the same fields. We can use conditionals to solve this:

```html
$if(author)$<meta name="author" content="$author$">$endif$

<!-- or, if you prefer -->

$if(author)$
  <meta name="author" content="$author$">
$endif$
```

Blog posts also should have a `description` and `keywords`, so let's add those:
to `posts/2015-12-07-tu-quoque.markdown`:

```md
---
title: My Blog Post
description: This is my great blog post
keywords: blog, first blog, best blog evar
author: I did it!
---
```

We'll then update our default template to handle those, as well:

```html
<title>$title$</title>
$if(author)$<meta name="author" content="$author$">$endif$
$if(keywords)$<meta name="keywords" content="$keywords$">$endif$
```

If you refresh http://localhost:5000/posts/2015-12-07-tu-quoque.html and open up
the web inspector, you'll now see that the `<head>` now contains not only your
post's `title`, but also all the other fields you specified!

There are many other possibilities for this, as well. For instance, if you
wanted to have different `og:type`s of pages, you could do:

```html
$if(type)$
  <meta property="og:type" content="$type$">
$else$
  <meta property="og:type" content="website">
$endif$
```

Check out the default template for this website here:
https://github.com/rpearce/robertwpearce.com/blob/main/src/templates/default.html.

## Rendering Partials

Lastly for today, what if we want to reuse templates and specify where they
should be rendered from other templates? Enter [hakyll
partials](https://jaspervdj.be/hakyll/tutorials/04-compilers.html#partials).

A common use of partials is for navigation across different templates. We can
add a new file, `templates/nav.html`, and place the following in it (add some
CSS classes and styling if you want it to look nice):

```html
<nav class="nav">
  <a href="/">Home</a>
  <a href="mailto:me@myemail.com">Email Me</a>
</nav>
```

Now, this partial can be used anywhere. For example, from
`templates/post.html`:

```html
$partial("templates/nav.html")$
```

## Wrapping Up

In this lesson we learned how to get started with
[hakyll](https://jaspervdj.be/hakyll) and learned some of the ways for us to get
started customizing it to our own needs. Next time, we'll dive into `site.hs`
to generate our own `sitemap.xml` file.

Next up:

* [Pt. 2 – Generating a Sitemap XML File](/hakyll-pt-2-generating-a-sitemap-xml-file.html)
* [Pt. 3 – Generating RSS and Atom XML Feeds](/hakyll-pt-3-generating-rss-and-atom-xml-feeds.html)
* [Pt. 4 – Copying Static Files For Your Build](/hakyll-pt-4-copying-static-files-for-your-build.html)
* [Pt. 5 – Generating Custom Post Filenames From a Title Slug](/hakyll-pt-5-generating-custom-post-filenames-from-a-title-slug.html)
* _(wip) Pt. 6 – Customizing Markdown Compiler Options_

* * *

Until next time,
<br />
Robert
