Defining Ema instance

Once you have model and route types in place, we must tell the Haskell compiler that they are suitable for generating static sites. We do this by creating an instance of the Ema typeclass.

Using some MyModel and the route Route shown in the previous section, we can create an instance as follows:

instance Ema MyModel Route where 
  -- Where to generate this route?
  encodeRoute _model = \case
    Index -> "index.html"
    About -> "about.html"

  -- Which route does this filepath correspond to?
  decodeRoute _model = \case
    "index.html" -> Just Index
    "about.html" -> Just About
    _ -> Nothing

  -- The third method is optional, and used by the `gen` command (not live-server)
  -- By default, Enum & Bounded will be used to determine this list.
  allRoutes _model =
    [Index, About]

The Ema typeclass has three methods, one of them being optional:

  • Define encodeRoute that converts our route type to a filepath
  • Define decodeRoute that does the reverse converstion (the conversion must be isomorphic)
  • Optionally, define allRoutes indicating the routes to statically generate


The Ema constraint is used by the runEma function that acts as the main entry point to your static site generator. It takes two arguments:

  • render function that renders your route (usually to HTML); we’ll go over this in the next section.
  • an IO action that takes LVar model as an argument.

This IO action is expected to be a long-running one, wherein you have full control over setting the value of the model over time.

Next, with our model and routes in place constrained by Ema type class, we will define the HTML for our site using Ema.

Links to this page
  • Working with routes

    Next, with our model and routes in place, we will make them work with Ema by defining their static site behaviour.

  • Tutorial
    It uses Ema.routeUrl function to create a URL out of our Route type. This function uses the Ema typeclass, so it uses the encodeRoute function defined further above.

    The runEma function is explained here, but in brief: it takes a render function (see below) as well as an IO action that allows us to create and update the model lvar. Note that threadDelay maxBound here? That is because our IO action must not exit; in the dev server mode of real-world websites, you would continue to monitor the external world (such as Markdown files) and update the model, to facilitate hot reload of data used by your site.

    We should now tell Ema how to convert our Route to actual file paths (which is used to determine the URL too). Let’s do that by making an instance of the Ema class:

  • Rendering HTML

    Once you have model and routes in place and constrained, the last piece of the puzzle is to write a function that takes both as arguments and returns file content (lazy bytestring, to be exact) to generate. This function can be as simple as the following:

  • Guide
    Defining Ema instanceConstrain your model and route to work with static sites
  • Defining your model

    Ema’s dev server supports hot reload; it will observe changes to your model, in addition to code. To facilitate this you will manage your model as a LVar. The runEma function (described here) takes an IO action that gets LVar model as an argument.