diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,9 @@
 # Changelog for skylighting-format-blaze-html
 
+## 0.1.2.2
+
+* Improve performance of blaze-html formatter.
+
 ## 0.1.2.1
 
 * Ensure that per-line anchors have aria-labels. Otherwise screen
diff --git a/skylighting-format-blaze-html.cabal b/skylighting-format-blaze-html.cabal
--- a/skylighting-format-blaze-html.cabal
+++ b/skylighting-format-blaze-html.cabal
@@ -1,5 +1,5 @@
 name:                skylighting-format-blaze-html
-version:             0.1.2.1
+version:             0.1.2.2
 synopsis:            HTML formatter for skylighting syntax highlighting library
 description:         This module allows tokens produced by skylighting-core
                      to be rendered as HTML.
@@ -22,7 +22,7 @@
 library
   exposed-modules:     Skylighting.Format.HTML
   other-extensions:    CPP
-  build-depends:       base >= 4.8 && < 5.0,
+  build-depends:       base >= 4.8 && < 5,
                        skylighting-core,
                        text,
                        containers,
diff --git a/src/Skylighting/Format/HTML.hs b/src/Skylighting/Format/HTML.hs
--- a/src/Skylighting/Format/HTML.hs
+++ b/src/Skylighting/Format/HTML.hs
@@ -60,8 +60,9 @@
                       . mconcat . intersperse (toHtml "\n")
                       . map (mapM_ (tokenToHtml opts))
 
--- | Format tokens as an HTML @pre@ block. Each line is wrapped in an a
--- element with the class ‘source-line’. If line numbering
+-- | Format tokens as an HTML @pre@ block. Each line is wrapped in a
+-- span element whose id is the line number (prefixed with 'lineIdPrefix'),
+-- beginning with an empty anchor linking to the line. If line numbering
 -- is selected, the surrounding pre is given the class ‘numberSource’,
 -- and the resulting html will display line numbers thanks to the included
 -- CSS.  See the documentation for 'formatHtmlInline' for information about how
@@ -70,7 +71,8 @@
 formatHtmlBlock = formatHtmlBlockFor Html5
 
 -- | Like 'formatHtmlBlock' but uses only attributes valid in HTML 4
--- (so, @aria-hidden@ is not used in empty line number spans).
+-- (so, no @aria-hidden@ or @aria-label@ attributes are used on the
+-- line anchors).
 formatHtml4Block :: FormatOptions -> [SourceLine] -> Html
 formatHtml4Block = formatHtmlBlockFor Html4
 
@@ -83,12 +85,13 @@
   H.pre ! A.class_ (toValue $ Text.unwords classes)
         $ wrapCode opts
         $ mconcat . intersperse (toHtml "\n")
-        $ zipWith (sourceLineToHtml htmlVersion opts) [startNum..] ls
+        $ zipWith (sourceLineToHtml htmlVersion opts idPrefix) [startNum..] ls
   where  classes = Text.pack "sourceCode" :
                    [Text.pack "numberSource" | numberLines opts] ++
                    [x | x <- containerClasses opts
                       , x /= Text.pack "sourceCode"]
          startNum = LineNo $ startNumber opts
+         idPrefix = Text.unpack (lineIdPrefix opts)
 
 wrapCode :: FormatOptions -> Html -> Html
 wrapCode opts h = H.code ! A.class_ (toValue $ Text.unwords
@@ -101,9 +104,10 @@
          startZero = startNumber opts - 1
 
 -- | Each line of source is wrapped in an (inline-block) anchor that makes
--- subsequent per-line processing (e.g. adding line numnbers) possible.
-sourceLineToHtml :: HtmlVersion -> FormatOptions -> LineNo -> SourceLine -> Html
-sourceLineToHtml htmlVersion opts lno cont =
+-- subsequent per-line processing (e.g. adding line numbers) possible.
+sourceLineToHtml :: HtmlVersion -> FormatOptions -> String -> LineNo -> SourceLine
+                 -> Html
+sourceLineToHtml htmlVersion opts idPrefix lno cont =
   H.span ! A.id lineNum
          $ do
            H.a ! A.href lineRef
@@ -121,15 +125,18 @@
            mapM_ (tokenToHtml opts) cont
   where  lineNum = toValue prefixedLineNo
          lineRef = toValue ('#':prefixedLineNo)
-         prefixedLineNo = Text.unpack (lineIdPrefix opts) <> show (lineNo lno)
+         prefixedLineNo = idPrefix <> show (lineNo lno)
 
 tokenToHtml :: FormatOptions -> Token -> Html
 tokenToHtml _ (NormalTok, txt)  = toHtml txt
 tokenToHtml opts (toktype, txt) =
   if titleAttributes opts
-     then sp ! A.title (toValue $ show toktype)
+     -- the class and title values are known to be HTML-safe, so we use
+     -- preEscapedToValue to skip escaping at render time
+     then sp ! A.title (preEscapedToValue (show toktype))
      else sp
-   where sp = H.span ! A.class_ (toValue $ short toktype) $ toHtml txt
+   where sp = H.span ! A.class_ (preEscapedToValue (short toktype))
+                     $ toHtml txt
 
 short :: TokenType -> String
 short KeywordTok        = "kw"
