diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,15 @@
 Brick changelog
 ---------------
 
+1.3
+---
+
+Package changes:
+* Removed dependency on `dlist`.
+
+Performance improvements:
+* Improved the performance of `vBox` and `hBox` (thanks Fraser Tweedale)
+
 1.2
 ---
 
diff --git a/brick.cabal b/brick.cabal
--- a/brick.cabal
+++ b/brick.cabal
@@ -1,5 +1,5 @@
 name:                brick
-version:             1.2
+version:             1.3
 synopsis:            A declarative terminal user interface library
 description:
   Write terminal user interfaces (TUIs) painlessly with 'brick'! You
@@ -123,7 +123,6 @@
                        bimap >= 0.5 && < 0.6,
                        data-clist >= 0.1,
                        directory >= 1.2.5.0,
-                       dlist,
                        exceptions >= 0.10.0,
                        filepath,
                        containers >= 0.5.7,
diff --git a/src/Brick/Widgets/Core.hs b/src/Brick/Widgets/Core.hs
--- a/src/Brick/Widgets/Core.hs
+++ b/src/Brick/Widgets/Core.hs
@@ -125,8 +125,8 @@
 import Control.Monad.State.Strict
 import Control.Monad.Reader
 import qualified Data.Foldable as F
+import Data.Traversable (for)
 import qualified Data.Text as T
-import qualified Data.DList as DL
 import qualified Data.Map as M
 import qualified Data.Set as S
 import qualified Data.IMap as I
@@ -642,30 +642,27 @@
       let availPrimary = c^.(contextPrimary br)
           availSecondary = c^.(contextSecondary br)
 
-          renderHis _ prev [] = return $ DL.toList prev
-          renderHis remainingPrimary prev ((i, prim):rest) = do
-              result <- render $ limitPrimary br remainingPrimary
-                               $ limitSecondary br availSecondary
-                               $ cropToContext prim
-              renderHis (remainingPrimary - (result^.imageL.(to $ imagePrimary br)))
-                        (DL.snoc prev (i, result)) rest
+          renderHi prim = do
+            remainingPrimary <- get
+            result <- lift $ render $ limitPrimary br remainingPrimary
+                                    $ limitSecondary br availSecondary
+                                    $ cropToContext prim
+            result <$ (put $! remainingPrimary - (result^.imageL.(to $ imagePrimary br)))
 
-      renderedHis <- renderHis availPrimary DL.empty his
+      (renderedHis, remainingPrimary) <-
+        runStateT (traverse (traverse renderHi) his) availPrimary
 
       renderedLows <- case lows of
           [] -> return []
           ls -> do
-              let remainingPrimary = c^.(contextPrimary br) -
-                                     (sum $ (^._2.imageL.(to $ imagePrimary br)) <$> renderedHis)
-                  primaryPerLow = remainingPrimary `div` length ls
+              let primaryPerLow = remainingPrimary `div` length ls
                   rest = remainingPrimary - (primaryPerLow * length ls)
-                  secondaryPerLow = c^.(contextSecondary br)
                   primaries = replicate rest (primaryPerLow + 1) <>
                               replicate (length ls - rest) primaryPerLow
 
               let renderLow ((i, prim), pri) =
                       (i,) <$> (render $ limitPrimary br pri
-                                       $ limitSecondary br secondaryPerLow
+                                       $ limitSecondary br availSecondary
                                        $ cropToContext prim)
 
               if remainingPrimary > 0 then mapM renderLow (zip ls primaries) else return []
@@ -673,11 +670,10 @@
       let rendered = sortBy (compare `DF.on` fst) $ renderedHis ++ renderedLows
           allResults = snd <$> rendered
           allImages = (^.imageL) <$> allResults
-          allPrimaries = imagePrimary br <$> allImages
-          allTranslatedResults = (flip map) (zip [0..] allResults) $ \(i, result) ->
-              let off = locationFromOffset br offPrimary
-                  offPrimary = sum $ take i allPrimaries
-              in addResultOffset off result
+          allTranslatedResults = flip evalState 0 $ for allResults $ \result -> do
+              offPrimary <- get
+              put $ offPrimary + (result ^. imageL . to (imagePrimary br))
+              pure $ addResultOffset (locationFromOffset br offPrimary) result
           -- Determine the secondary dimension value to pad to. In a
           -- vertical box we want all images to be the same width to
           -- avoid attribute over-runs or blank spaces with the wrong
