lucid2: Clear to write, read and edit DSL for HTML

[ bsd3, library, web ] [ Propose Tags ]

Clear to write, read and edit DSL for HTML.

  • Names are consistent, and do not conflict with base or are keywords (all have suffix _)

  • Same combinator can be used for attributes and elements (e.g. style_)

  • For more, read the blog post

See the Lucid module for more documentation.

This package is the newer version of lucid.

[Skip to Readme]


Note: This package has metadata revisions in the cabal description newer than included in the tarball. To unpack the package including the revisions, use 'cabal get'.

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS] 0.0.20220508, 0.0.20220509, 0.0.20220526, 0.0.20221012
Change log
Dependencies base (>=4.8 && <4.19), blaze-builder, bytestring (>=, containers (>=, mtl (>=2.2.2), text (>=, transformers (>= [details]
License BSD-3-Clause
Copyright 2014-2022 Chris Done
Author Chris Done
Revised Revision 1 made by ChrisDone at 2023-05-29T17:35:04Z
Category Web
Home page
Source repo head: git clone
Uploaded by ChrisDone at 2022-10-12T14:40:09Z
Distributions LTSHaskell:0.0.20221012, NixOS:0.0.20221012, Stackage:0.0.20221012
Reverse Dependencies 2 direct, 0 indirect [details]
Downloads 429 total (33 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2022-10-12 [all 1 reports]

Readme for lucid2-0.0.20221012

[back to package description]


Note: For a list of changes from lucid1 to lucid2, see

Clear to write, read and edit DSL for writing HTML

Table of Contents


This is version 2 of the lucid package, according to the Immutable Publishing Policy.

There never be any breaking changes made to this package.


HTML terms in Lucid are written with a postfix ‘_’ to indicate data rather than code. Some examples:

p_, class_, table_, style_

See Lucid.Html5 for a complete list of Html5 combinators.

Plain text is written using the OverloadedStrings and ExtendedDefaultRules extensions, and is automatically escaped:

λ> "123 < 456" :: Html ()
123 &lt; 456

Elements nest by function application:

λ> table_ (tr_ (td_ (p_ "Hello, World!"))) :: Html ()
<table><tr><td><p>Hello, World!</p></td></tr></table>

Elements are juxtaposed via monoidal append:

λ> p_ "hello" <> p_ "sup" :: Html ()

Or monadic sequencing:

λ> div_ (do p_ "hello"; p_ "sup") :: Html ()

Attributes are set by providing an argument list:

λ> p_ [class_ "brand"] "Lucid Inc" :: Html ()
<p class="brand">Lucid Inc</p>

Here is a fuller example of Lucid:

table_ [rows_ "2"]
       (tr_ (do td_ [class_ "top",colspan_ "2",style_ "color:red"]
                    (p_ "Hello, attributes!")
                td_ "yay!"))
<table rows="2">
    <td style="color:red" colspan="2" class="top">
      <p>Hello, attributes!</p>


For proper rendering you can easily run some HTML immediately with:

λ> renderText (p_ "Hello!")

Or to bytes:

λ> renderBS (p_ [style_ "color:red"] "Hello!")
"<p style=\"color:red\">Hello!</p>"

For ease of use in GHCi, there is a Show instance, as demonstrated above.

If the above rendering functions aren't suited for your purpose, you can run the monad directly via execHtml and use the more low-level blaze Builder, which has a plethora of output modes in Blaze.ByteString.Builder.

See the documentation for the Lucid module for information about using it as a monad transformer.

Good to know

  • Attributes are escaped, so you cannot write arbitrary JavaScript in attributes. Instead, do something like onclick_ "foo()".
  • Attributes are rendered in the order that they are written in your Haskell code.


You can use lift to call parent monads.

λ> runReader (renderTextT (html_ (body_ (do name <- lift ask
                                            p_ [class_ "name"] (toHtml name)))))
             ("Chris" :: String)
"<html><body><p class=\"name\">Chris</p></body></html>"