diff --git a/lucid.cabal b/lucid.cabal
--- a/lucid.cabal
+++ b/lucid.cabal
@@ -1,5 +1,5 @@
 name:                lucid
-version:             2.9.6
+version:             2.9.7
 synopsis:            Clear to write, read and edit DSL for HTML
 description:         Clear to write, read and edit DSL for HTML. See the 'Lucid' module
                      for description and documentation.
@@ -32,6 +32,8 @@
                    , text
                    , transformers
                    , unordered-containers
+  if !impl(ghc >= 8.0)
+    build-depends:   semigroups
 
 test-suite test
     type: exitcode-stdio-1.0
diff --git a/src/Lucid.hs b/src/Lucid.hs
--- a/src/Lucid.hs
+++ b/src/Lucid.hs
@@ -46,9 +46,17 @@
 -- indicate that you want HTML. In normal code your top-level
 -- declaration signatures handle that.
 --
--- Plain text is written using the @OverloadedStrings@ and
--- @ExtendedDefaultRules@ extensions, and is automatically escaped:
+-- For GHCi:
 --
+-- @
+-- :set -XOverloadedStrings -XExtendedDefaultRules@
+-- import Lucid
+-- @
+--
+-- In a module: @{-\# LANGUAGE OverloadedStrings, ExtendedDefaultRules \#-}@
+--
+-- Plain text is written like this, and is automatically escaped:
+--
 -- >>> "123 < 456" :: Html ()
 -- 123 &lt; 456
 --
@@ -62,7 +70,7 @@
 -- >>> table_ (tr_ (td_ (p_ "Hello, World!"))) :: Html ()
 -- <table><tr><td><p>Hello, World!</p></td></tr></table>
 --
--- Elements are juxtaposed via monoidal append:
+-- Elements are juxtaposed via monoidal append (remember to import "Data.Monoid"):
 --
 -- >>> p_ "hello" <> p_ "sup" :: Html ()
 -- <p>hello</p><p>sup</p>
diff --git a/src/Lucid/Base.hs b/src/Lucid/Base.hs
--- a/src/Lucid/Base.hs
+++ b/src/Lucid/Base.hs
@@ -5,6 +5,9 @@
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE DeriveDataTypeable #-}
 
+-- Search for UndecidableInstances to see why this is needed
+{-# LANGUAGE UndecidableInstances #-}
+
 -- | Base types and combinators.
 
 module Lucid.Base
@@ -42,6 +45,7 @@
 import           Control.Monad
 import           Control.Monad.Morph
 import           Control.Monad.Reader
+import           Control.Monad.State.Class (MonadState(..))
 import           Data.ByteString.Lazy (ByteString)
 import qualified Data.ByteString.Lazy as L
 import qualified Data.ByteString as S
@@ -49,7 +53,8 @@
 import           Data.HashMap.Strict (HashMap)
 import qualified Data.HashMap.Strict as M
 import           Data.Hashable (Hashable(..))
-import           Data.Monoid
+import           Data.Semigroup (Semigroup (..))
+import           Data.Monoid (Monoid (..))
 import           Data.String
 import           Data.Text (Text)
 import qualified Data.Text.Lazy as LT
@@ -96,6 +101,9 @@
 instance MFunctor HtmlT where
   hoist f (HtmlT xs) = HtmlT (f xs)
 
+instance (a ~ (),Monad m) => Semigroup (HtmlT m a) where
+  (<>) = liftM2 mappend
+
 -- | Monoid is right-associative, a la the 'Builder' in it.
 instance (a ~ (),Monad m) => Monoid (HtmlT m a) where
   mempty  = return mempty
@@ -124,6 +132,18 @@
     HtmlT (do a <- m
               return (\_ -> mempty,a))
 
+-- MonadReader / MonadState instances need UndecidableInstances,
+-- because they do not satisfy the coverage condition.
+
+instance MonadReader r m => MonadReader r (HtmlT m) where
+  ask = lift ask
+  local f (HtmlT a) = HtmlT (local f a)
+
+instance MonadState s m => MonadState s (HtmlT m) where
+  get = lift get
+  put = lift . put
+  state = lift . state
+
 -- | If you want to use IO in your HTML generation.
 instance MonadIO m => MonadIO (HtmlT m) where
   liftIO = lift . liftIO
@@ -427,7 +447,7 @@
 
 -- | Folding and monoidally appending attributes.
 foldlMapWithKey :: Monoid m => (k -> v -> m) -> HashMap k v -> m
-foldlMapWithKey f = M.foldlWithKey' (\m k v -> m <> f k v) mempty
+foldlMapWithKey f = M.foldlWithKey' (\m k v -> m `mappend` f k v) mempty
 
 -- | Convenience function for constructing builders.
 s :: String -> Builder
