---
author: "Robert Pearce"
authorTwitter: "@RobertWPearce"
desc: "This short hakyll tutorial will show you a simple way to copy static files over to your build folder."
keywords: "hakyll, copyFileCompiler, copying files, hakyll tutorial, hakyll blog, hakyll blog tutorial, hakyll static site, static site generator"
title: "Hakyll Pt. 4 – Copying Static Files For Your Build"
updated: "2023-02-11T16:00:00Z"
---

This is part 4 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](/hakyll-pt-1-setup-initial-customization.html)
* [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
* [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

You will inevitably need to copy static files over to your build folder at some
point in a hakyll project, and this short tutorial will show you a simple way to
do so.

1. [Copying Files the Long Way](#copying-files-the-long-way)
1. [Simplify File Copying With a List](#simplify-file-copying-with-a-list)
1. [Simplify File Copying With Pattern Composition Operators](#simplify-file-copying-with-pattern-composition-operators)
1. [GitHub Pages Tip for Dotfiles and Dotfolders](#github-pages-tip-for-dotfiles-and-dotfolders)

## Copying Files the Long Way

As of the time of this writing, the [default hakyll example for copying files](https://github.com/jaspervdj/hakyll/blob/a983c8cbc917ffa3ce81d2540b50bdb321588b92/data/example/site.hs#L10-L12)
looks like this:

```haskell
match "images/*" $ do
    route   idRoute
    compile copyFileCompiler
```

This is great and gets the job done! When I first looked at copying more files,
I went down this path:

```haskell
match "CNAME" $ do
    route   idRoute
    compile copyFileCompiler


match "robots.txt" $ do
    route   idRoute
    compile copyFileCompiler


match "images/*" $ do
    route   idRoute
    compile copyFileCompiler


match "fonts/*" $ do
    route   idRoute
    compile copyFileCompiler


-- ...and so on
```

Obviously, there is some code duplication here; there must be a better way!

## Simplify File Copying With a List

Here are all the items I need copied over:

```text
CNAME
robots.txt
_config.yml
images/*
fonts/*
.well-known/*
```

As it turns out, this _list_ of file identifiers to copy can be used in
conjunction with [`forM_`](https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Foldable.html#v:forM_)
to take some foldable structure (for us, a list), map each element to a monadic
action that uses hakyll's `match` function, ignore the results and ultimately
simplify our code.

The type signature for `forM_` is as follows:

```haskell
forM_ :: (Foldable t, Monad m) => t a -> (a -> m b) -> m ()
```

And here is the implementation:

```haskell
forM_ [ "CNAME"
      , "robots.txt"
      , "_config.yml"
      , "images/*"
      , "fonts/*"
      , ".well-known/*"
      ] $ \f -> match f $ do
    route   idRoute
    compile copyFileCompiler
```

Nice! While this technique is not mentioned in the documentation, it is present
in the [hakyll website's `site.hs` file](https://github.com/jaspervdj/hakyll/blob/1abdeee743d65d96c6f469213ca6e7ea823340a7/web/site.hs#L27-L29),
so we know we're in good company if [jaspervdj](https://github.com/jaspervdj) is
already using it.

If you want to read more about the possible patterns that can be matched, check
out the commentary in the source here: https://github.com/jaspervdj/hakyll/blob/1abdeee743d65d96c6f469213ca6e7ea823340a7/lib/Hakyll/Core/Identifier/Pattern.hs.

## Simplify File Copying With Pattern Composition Operators

[In this /r/haskell reddit thread](https://www.reddit.com/r/haskell/comments/ak9700/hakyll_pt_4_copying_static_files_for_your_build/ef3lv73/)
by [GAumala](https://www.reddit.com/user/GAumala), they point out that [hakyll's
pattern composition operators](https://jaspervdj.be/hakyll/reference/Hakyll-Core-Identifier-Pattern.html#g:3)
can also be used to accomplish the same goal. Here is how we would could convert
our `forM_` above to instead use [`.||.`](https://jaspervdj.be/hakyll/reference/Hakyll-Core-Identifier-Pattern.html#v:.-124--124-.):

```haskell
match ("CNAME"
       .||. "favicon.ico"
       .||. "robots.txt"
       .||. "_config.yml"
       .||. "images/*"
       .||. "fonts/*"
       .||. ".well-known/*") $ do
    route   idRoute
    compile copyFileCompiler
```

While I understand the `forM_` better, this does seem to be more attractive!

## GitHub Pages Tip for Dotfiles and Dotfolders

If you're using GitHub pages and have any dotfiles or dotfolders to copy over,
make sure you pay attention here.

Let's say you have signed up for [Brave
Payments](https://publishers.basicattentiontoken.org) and need to verify your
site by placing a file at:

```text
https://mysite.com/.well-known/brave-payments-verification.txt
```

Unfortunately, [GitHub Pages](https://pages.github.com), which uses
[jekyll](https://jekyllrb.com) under the hood, will ignore your dotfiles and
dotfolders by default and will therefore not deploy them.

We can fix this by adding a `_config.yml` file to our project (you can see it
included in the list in the previous section) and telling it to include what it
is ignoring:

```yaml
# _config.yml

include: [".well-known"]
```

Once you've done this, you can commit this file, push it up to GitHub and view
it on your published site.

You can read more about jekyll's configuration options here:
https://jekyllrb.com/docs/configuration/options/.

## Wrapping Up

Today we learned a simple way to list what files we want to be copied over in
our hakyll projects, got exposed to `forM_` and uncovered a potential issue
with dotfiles and dotfolders not getting published on GitHub Pages.

Next up:

* [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_

* * *

Thank you for reading!
<br />
Robert
