Hakyll Pt. 1 – Setup & Initial Customization
Info
Summary | Use the hakyll static site generator to set up a website or blog. |
---|---|
Shared | 2018-11-04 |
Revised | 2023-02-11 @ 16:00 UTC |
This is part 1 of a multipart series where we will look at getting a website / blog set up with hakyll and customized a fair bit.
- Pt. 1 – Setup & Initial Customization
- Pt. 2 – Generating a Sitemap XML File
- Pt. 3 – Generating RSS and Atom XML Feeds
- Pt. 4 – Copying Static Files For Your Build
- Pt. 5 – Generating Custom Post Filenames From a Title Slug
- Pt. 6 – Pure Builds With Nix
- The hakyll-nix-template Tutorial
Overview
Installation & Setup
While this is detailed fully on the hakyll installation tutorial, I will repeat it here.
- install stack and make sure
$HOME/.local/bin
is included in yourPATH
λ stack install hakyll
– should installhakyll-init
in$HOME/.local/bin
λ hakyll-init ourblog.com
λ cd ourblog.com
λ stack init
λ stack build
λ stack exec site build
λ stack exec site rebuild
– to test the rebuild commandλ stack exec site watch
– starts dev server & watches for changes- navigate to http://localhost:8000 to see it!
Configuration Rules
Hakyll gives you the ability to override its existing configuration rules 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):
-- | 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 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:
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:
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:
---
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 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:
<title>$title$</title>
Cool! Now what if we wanted to use our author
metadata?
<meta name="author" content="$author$">
Oh no!
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:
$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
:
---
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:
<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:
$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.
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):
<nav class="nav">
<a href="/">Home</a>
<a href="mailto:[email protected]">Email Me</a>
</nav>
Now, this partial can be used anywhere. For example, from templates/post.html
:
$partial("templates/nav.html")$
Wrapping Up
In this lesson we learned how to get started with 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
- Pt. 3 – Generating RSS and Atom XML Feeds
- Pt. 4 – Copying Static Files For Your Build
- Pt. 5 – Generating Custom Post Filenames From a Title Slug
- (wip) Pt. 6 – Customizing Markdown Compiler Options
Until next time,
Robert