diff --git a/BlogLiterately.cabal b/BlogLiterately.cabal
--- a/BlogLiterately.cabal
+++ b/BlogLiterately.cabal
@@ -1,5 +1,5 @@
 Name:           BlogLiterately
-Version:        0.8.7
+Version:        0.8.8
 Synopsis:       A tool for posting Haskelly articles to blogs
 Description:    Write blog posts in Markdown format, then use BlogLiterately
                 to do syntax highlighting, format ghci sessions, and upload
@@ -26,7 +26,7 @@
 Maintainer:     Brent Yorgey <byorgey@gmail.com>
 Stability:      experimental
 Build-Type:     Simple
-Tested-With:    GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.3 || ==8.10.1
+Tested-With:    GHC ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.2.1
 Extra-Source-Files: CHANGES.md
                     README.markdown
                     doc/BlogLiteratelyDoc.lhs
@@ -39,7 +39,7 @@
   location: git://github.com/byorgey/BlogLiterately.git
 
 Library
-  Build-Depends:   base >= 4.0 && < 4.15,
+  Build-Depends:   base >= 4.0 && < 4.17,
                    process,
                    filepath,
                    directory,
@@ -47,25 +47,25 @@
                    containers,
                    bool-extras,
                    mtl,
-                   text >= 1.2 && < 1.3,
+                   text >= 1.2 && < 2.1,
                    temporary >= 1.1 && < 1.4,
-                   strict >= 0.3 && < 0.4,
+                   strict >= 0.3 && < 0.5,
                    split >= 0.1.4 && < 0.3,
-                   transformers >= 0.3 && < 0.6,
+                   transformers >= 0.3 && < 0.7,
                    parsec >= 3 && < 3.2,
                    HaXml >= 1.22 && < 1.26,
                    hscolour >= 1.20 && < 1.25,
                    blaze-html >= 0.5 && < 0.10,
                    cmdargs >= 0.9.5 && < 0.11,
                    haxr >= 3000.11 && < 3000.12,
-                   pandoc >= 2.0 && < 2.11,
-                   pandoc-types >= 1.20 && < 1.22,
-                   pandoc-citeproc >= 0.1.2 && < 0.18,
+                   pandoc >= 2.8 && < 2.19,
+                   pandoc-types >= 1.20 && < 1.23,
+                   citeproc >= 0.5 && < 0.8,
                    highlighting-kate >= 0.5 && < 0.7,
                    data-default >= 0.5 && < 0.8,
-                   lens >= 3.8 && < 4.20,
+                   lens >= 3.8 && < 5.2,
                    tagsoup >= 0.13.4 && < 0.15,
-                   HTTP >= 4000.3 && < 4000.4
+                   HTTP >= 4000.3 && < 4000.5
   Exposed-modules: Text.BlogLiterately
                    Text.BlogLiterately.Block
                    Text.BlogLiterately.Ghci
diff --git a/src/Text/BlogLiterately/Transform.hs b/src/Text/BlogLiterately/Transform.hs
--- a/src/Text/BlogLiterately/Transform.hs
+++ b/src/Text/BlogLiterately/Transform.hs
@@ -1,9 +1,7 @@
 {-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards   #-}
 {-# LANGUAGE TupleSections     #-}
-{-# LANGUAGE TypeOperators     #-}
 
 -----------------------------------------------------------------------------
 -- |
@@ -46,7 +44,7 @@
     , postLink
 
       -- * Transforms
-    , Transform(..), pureTransform, ioTransform, runTransform, runTransforms
+    , Transform(..), pureTransform, ioTransform, pioTransform, runTransform, runTransforms
 
       -- * Transforming documents
     , xformDoc
@@ -82,9 +80,9 @@
 import           System.FilePath                   (takeExtension, (<.>), (</>))
 import           System.IO                         (hFlush, stdout)
 import           Text.Blaze.Html.Renderer.String   (renderHtml)
-import           Text.CSL.Pandoc                   (processCites')
 import           Text.HTML.TagSoup
 import           Text.Pandoc                       hiding (openURL)
+import           Text.Pandoc.Citeproc              (processCitations)
 import           Text.Pandoc.Error                 (PandocError)
 import           Text.Parsec                       (ParseError)
 
@@ -130,7 +128,7 @@
 --   For examples, see the implementations of the standard transforms
 --   below.
 data Transform = Transform
-                 { getTransform :: StateT (BlogLiterately, Pandoc) IO ()
+                 { getTransform :: StateT (BlogLiterately, Pandoc) PandocIO ()
                    -- ^ A document transformation, which can transform
                    --   both the document and the options and have
                    --   effects in the IO monad.  The options record
@@ -143,22 +141,29 @@
 -- | Construct a transformation from a pure function.
 pureTransform :: (BlogLiterately -> Pandoc -> Pandoc)
               -> (BlogLiterately -> Bool) -> Transform
-pureTransform transf cond = Transform (gets fst >>= \bl -> _2 %= transf bl) cond
+pureTransform transf = Transform (gets fst >>= \bl -> _2 %= transf bl)
 
 -- | Construct a transformation from a function in the @IO@ monad.
 ioTransform :: (BlogLiterately -> Pandoc -> IO Pandoc)
             -> (BlogLiterately -> Bool) -> Transform
-ioTransform transf cond = Transform (StateT . fmap (fmap $ (,) ()) $ transf') cond
-  where transf' (bl,p) = ((,) bl) <$> transf bl p
+ioTransform transf = pioTransform (\b p -> liftIO (transf b p))
 
+-- | Construct a transformation from a function in the @PandocIO@ monad.
+pioTransform :: (BlogLiterately -> Pandoc -> PandocIO Pandoc)
+             -> (BlogLiterately -> Bool) -> Transform
+pioTransform transf = Transform $ do
+  (bl,p) <- get
+  p' <- lift $ transf bl p
+  put (bl,p')
+
 -- | Run a 'Transform' (if its condition is met).
-runTransform :: Transform -> StateT (BlogLiterately, Pandoc) IO ()
+runTransform :: Transform -> StateT (BlogLiterately, Pandoc) PandocIO ()
 runTransform t = do
   bl <- gets fst
   when (xfCond t bl) $ getTransform t
 
 -- | Run a pipeline of 'Transform's.
-runTransforms :: [Transform] -> BlogLiterately -> Pandoc -> IO (BlogLiterately, Pandoc)
+runTransforms :: [Transform] -> BlogLiterately -> Pandoc -> PandocIO (BlogLiterately, Pandoc)
 runTransforms ts bl p = execStateT (mapM_ runTransform ts) (bl,p)
 
 --------------------------------------------------
@@ -450,7 +455,7 @@
 
 -- | Format citations.
 citationsXF :: Transform
-citationsXF = ioTransform (const processCites') citations'
+citationsXF = pioTransform (const processCitations) citations'
 
 -- | Load options from a profile if one is specified.
 profileXF :: Transform
@@ -547,7 +552,7 @@
     (     fixLineEndings
       >>> T.pack
       >>> parseFile parseOpts
-      >=> (liftIO . runTransforms xforms bl)
+      >=> runTransforms xforms bl
       >=> (\(bl', p) -> (bl',) <$> writeHtml5String (writeOpts bl' tpl) p)
       >=> _2 (return . T.unpack)
     )
