Clear to write, read and edit DSL for HTML

Version on this page:2.9.7
LTS Haskell 11.1:2.9.10
Stackage Nightly 2018-03-23:2.9.10
Latest on Hackage:2.9.10

See all snapshots lucid appears in

BSD3 licensed by Chris Done
Maintained by,

Module documentation for 2.9.7

lucid Hackage Build Status

Clear to write, read and edit DSL for writing HTML


lucid-from-html will convert html to the lucid DSL, though it is experimental.


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.


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>"



  • Drop GHC-7.8 and older (pre-AMP) support
  • Generalise type-signatures to require only Applicative or Functor, when that's enough


  • Add commuteHtmlT to commute HtmlT m a into m (HtmlT n a).
  • Add MonadError e m => MonadError e (HtmlT m) and MonadWriter w m => MonadWriter w (HtmlT m) instances

  • Improve performance by adding INLINE pragmas to Monad etc. combinators.


  • Add integrity_, crossorigin_ attributes
  • Add classes_ smart attribute constructor
  • Add ToHtml (HtmlT m a) instance


  • Add Semigroup (HtmlT m a) instance
  • Add MonadState and MonadReader instances


  • Fix compilation of benchmarks
  • Add @athanclark's version of relaxHtmlT
  • Add a utility to generalize the underlying monad from Identity: relaxHtmlT


  • Add ToHtml instance for ByteString (both)
  • Add MFunctor HtmlT instance, i.e. hoist from @mmorph@.


  • Small performance tweaks.
  • Make svg_ an element.


  • Restrict monoid instance's a to ~ () (means you can use mempty without inference errors)


  • Export renderToFile from top-level Lucid module.


  • Add some extra HTML tags.


  • Use variadic HTML terms.
  • Add lazy Text instance for ToHtml.


  • Initial version.
comments powered byDisqus