Rendering HTML with Blaze and Lucid

Spock accepts HTML from any template library. With Blaze, render to lazy Text and convert it for html, or render directly to UTF-8 bytes and send them with lazyBytes. Lucid provides the same choice.

Run the example

The complete example and response tests are built by the repository’s Cabal and Stack CI with GHC 9.14.1. From a checkout:

cabal run spock-rendering-example

Visit http://localhost:8080/, /utf8, and /lucid. Each route accepts a message query parameter, for example /utf8?message=Hello. To run the tests, use cabal test spock-rendering-example.

For a separate application, follow the getting started setup and add blaze-html, lucid, and text to its dependencies. The example uses Spock-core because rendering does not need sessions or a database. These same actions are available through Web.Spock in a full Spock application.

Blaze with Text

{-# LANGUAGE OverloadedStrings #-}

import qualified Data.Text.Lazy as TL
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html.Renderer.Text as R
import Web.Spock.Core

hello :: H.Html
hello = H.p "Hello, λ!"

app :: SpockT IO ()
app = get root $
  html $ TL.toStrict $ R.renderHtml hello

R.renderHtml produces lazy Text. html accepts strict Text, encodes it as UTF-8, and sets Content-Type: text/html; charset=utf-8. Calling it finishes the action; set any status or other headers first.

Blaze with UTF-8 bytes

Replace the renderer import and action with:

import qualified Text.Blaze.Html.Renderer.Utf8 as R

app :: SpockT IO ()
app = get root $ do
  setHeader "Content-Type" "text/html; charset=utf-8"
  lazyBytes $ R.renderHtml hello

The qualified R.renderHtml now returns a lazy ByteString, so it can go directly to lazyBytes. Set the content type yourself: lazyBytes accepts arbitrary bytes and does not choose a media type. This path avoids converting through Text; it does not require any internal Spock functions.

Lucid

Lucid’s renderBS also produces a lazy UTF-8 ByteString:

import qualified Lucid as L

app :: SpockT IO ()
app = get root $ do
  setHeader "Content-Type" "text/html; charset=utf-8"
  lazyBytes $ L.renderBS $ L.p_ "Hello, λ!"

Alternatively, use html . TL.toStrict . L.renderText for the Text path.

Rendering request data

Use Blaze’s H.toHtml or Lucid’s L.toHtml to insert text supplied by a user. They escape HTML characters such as < and &. The executable’s three routes and tests cover Unicode and escaped request data. Reserve raw/pre-escaped HTML operations for markup you control; do not concatenate user input into markup.

For JSON, use json; for plain text, use text. Both set the corresponding content type. See the API reference and its Web.Spock.Action module for headers, files, streaming, and other response helpers.