diff --git a/Text/XML/Expat/Format.hs b/Text/XML/Expat/Format.hs
--- a/Text/XML/Expat/Format.hs
+++ b/Text/XML/Expat/Format.hs
@@ -154,7 +154,9 @@
        -> c B.ByteString
 formatSAXG l1 = joinL $ do
     it1 <- runList l1
-    return $ case it1 of
+    return $ formatItem it1
+  where
+    formatItem it1 = case it1 of
         Nil -> mzero
         Cons (StartElement name attrs) l2 ->
             fromList (startTagHelper name attrs)
@@ -167,7 +169,7 @@
                             formatSAXG l3
                         _ ->
                             cons (B.singleton (c2w '>')) $
-                            formatSAXG l2
+                            formatItem it2
             )
         Cons (EndElement name) l2 ->
             cons (pack "</") $
@@ -217,10 +219,13 @@
         -> n c tag text
 indent_ cur perLevel elt | isElement elt =
     flip modifyChildren elt $ \chs -> joinL $ do
-        anyElts <- anyElements chs
+        (anyElts, chs') <- anyElements [] chs
+        -- The new list chs' is the same as the old list chs, but some of its
+        -- nodes have been loaded into memory.  This is to avoid evaluating
+        -- list elements twice.
         if anyElts
-            then addSpace True chs
-            else return chs
+            then addSpace True chs'
+            else return chs'
   where
     addSpace :: Bool -> c (n c tag text) -> ItemM c (c (n c tag text))
     addSpace startOfText l = do
@@ -244,13 +249,27 @@
                 return $
                     cons n $
                     joinL $ addSpace False l'
-    anyElements :: c (n c tag text) -> ItemM c Bool
-    anyElements l = do
+
+    -- acc is used to keep the nodes we've scanned into memory.
+    -- We then construct a new list that looks the same as the old list, but
+    -- which starts with the nodes in memory, to prevent the list being
+    -- demanded more than once (in case it's monadic and it's expensive to
+    -- evaluate).
+    anyElements :: [n c tag text]   -- ^ Accumulator for tags we've looked at.
+                -> c (n c tag text)
+                -> ItemM c (Bool, c (n c tag text))
+    anyElements acc l = do
         n <- runList l
         case n of
-            Nil                    -> return False
-            Cons n _ | isElement n -> return True
-            Cons _ l'              -> anyElements l'
+            Nil                     -> return (False, instantiatedList acc mzero)
+            Cons n l' | isElement n -> return (True,  instantiatedList (n:acc) l')
+            Cons n l'               -> anyElements (n:acc) l'
+      where
+        instantiatedList :: [n c tag text] -> c (n c tag text) -> c (n c tag text)
+        instantiatedList acc l' = reverse acc `prepend` l'
+
+        prepend :: forall a . [a] -> c a -> c a
+        prepend xs l = foldr cons l xs
 
     strip t | gxNullString t = Nothing
     strip t | isSpace (gxHead t) = strip (gxTail t)
diff --git a/hexpat.cabal b/hexpat.cabal
--- a/hexpat.cabal
+++ b/hexpat.cabal
@@ -1,6 +1,6 @@
 Cabal-Version: >= 1.6
 Name: hexpat
-Version: 0.18.2
+Version: 0.18.3
 Synopsis: XML parser/formatter based on expat
 Description:
   This package provides a general purpose Haskell XML library using Expat to
@@ -42,7 +42,8 @@
   ChangeLog: 0.15 changes intended to fix a (rare) \"error: a C finalizer called back into Haskell.\"
     that seemed only to happen only on ghc6.12.X; 0.15.1 Fix broken Annotated parse;
     0.16 switch from mtl to transformers; 0.17 fix mapNodeContainer & rename some things.;
-    0.18 rename defaultEncoding to overrideEncoding.
+    0.18 rename defaultEncoding to overrideEncoding. 0.18.3 formatG and indent were demanding list
+    items more than once (inefficient in chunked processing).
 Category: XML
 License: BSD3
 License-File: LICENSE
@@ -80,12 +81,12 @@
     base >= 3 && < 5,
     bytestring,
     transformers,
-    text >= 0.5,
-    utf8-string >= 0.3.3,
-    deepseq >= 1.1.0.0,
+    text >= 0.5 && < 0.8,
+    utf8-string == 0.3.*,
+    deepseq == 1.1.*,
     containers,
-    extensible-exceptions >= 0.1 && < 0.2,
-    List >= 0.4
+    extensible-exceptions == 0.1.*,
+    List == 0.4.*
   Exposed-Modules:
     Text.XML.Expat.Annotated,
     Text.XML.Expat.Cursor,
diff --git a/test/suite/Text/XML/Expat/UnitTests.hs b/test/suite/Text/XML/Expat/UnitTests.hs
--- a/test/suite/Text/XML/Expat/UnitTests.hs
+++ b/test/suite/Text/XML/Expat/UnitTests.hs
@@ -171,13 +171,13 @@
                      "<test>\n  <ignorance>strength</ignorance>\n  <freedom>Slavery</freedom>\n  <war>Peace</war>\n</test>"),
                 ("#5",
                  toByteString $ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"++
-                     "<test><ministries><mini name=\"minitrue\">Ministry of Truth</mini>In between"++
+                     "<test>Extra here<ministries><mini name=\"minitrue\">Ministry of Truth</mini>In between"++
                      "<mini name=\"minilove\">Ministry of Love</mini>\n  And some more"++
                      "<mini name=\"miniplenty\">Ministry of Plenty</mini>"++
                      "<mini name=\"minipax\">Ministry of Peace<at-war-with>Eurasia</at-war-with></mini></ministries>"++
                      "<wisdom><ignorance>strength</ignorance></wisdom></test>",
                  toByteString $ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"++
-                     "<test>\n  <ministries>\n    <mini name=\"minitrue\">Ministry of Truth</mini>In between"++
+                     "<test>Extra here\n  <ministries>\n    <mini name=\"minitrue\">Ministry of Truth</mini>In between"++
                      "\n    <mini name=\"minilove\">Ministry of Love</mini>And some more"++
                      "\n    <mini name=\"miniplenty\">Ministry of Plenty</mini>"++
                      "\n    <mini name=\"minipax\">Ministry of Peace\n      <at-war-with>Eurasia</at-war-with>\n    </mini>\n  </ministries>"++
