diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,7 @@
+## 1.10.1.0
+* Add `document` function to wrap a stream of XML events in an 'EventBeginDocument'/'EventEndDocument' pair.
+* Ensure compatibility with GHC 9.10
+
 ## 1.10.0.1
 * Fix doctest on GHC 9.12
 
diff --git a/src/Text/XML/Stream/Render.hs b/src/Text/XML/Stream/Render.hs
--- a/src/Text/XML/Stream/Render.hs
+++ b/src/Text/XML/Stream/Render.hs
@@ -19,6 +19,7 @@
     orderAttrs,
 
     -- * Event rendering
+    document,
     tag,
     content,
 
diff --git a/src/Text/XML/Stream/Render/Internal.hs b/src/Text/XML/Stream/Render/Internal.hs
--- a/src/Text/XML/Stream/Render/Internal.hs
+++ b/src/Text/XML/Stream/Render/Internal.hs
@@ -21,6 +21,7 @@
     , rsXMLDeclaration
     , orderAttrs
       -- * Event rendering
+    , document
     , tag
     , content
       -- * Attribute rendering
@@ -82,7 +83,11 @@
       --
       -- @since 1.3.3
     , rsXMLDeclaration :: Bool
-      -- ^ Determines whether the XML declaration will be output.
+      -- ^ Determines whether the XML declaration will be output. Note that when
+      -- using the streaming API the XML declaration will be output only if this
+      -- is set to true /and/ the stream includes an 'EventBeginDocument' event.
+      -- Apart from yielding it explicitly, this can be achieved by wrapping the
+      -- stream in the 'document' function.
       --
       -- Default: @True@
       --
@@ -391,6 +396,14 @@
         | k `Set.member` used = (dlist, used)
         | otherwise = (dlist . ((k, v):), Set.insert k used)
 
+-- | Wrap the given stream in an 'EventBeginDocument'/'EventEndDocument' pair.
+--
+-- @since 1.10.1.0
+document :: (Monad m) => ConduitT i Event m () -> ConduitT i Event m ()
+document content' = do
+  yield EventBeginDocument
+  content'
+  yield EventEndDocument
 
 -- | Generate a complete XML 'Element'.
 tag :: (Monad m) => Name -> Attributes -> ConduitT i Event m ()  -- ^ 'Element''s subnodes.
diff --git a/test/unit.hs b/test/unit.hs
--- a/test/unit.hs
+++ b/test/unit.hs
@@ -25,6 +25,7 @@
                                                (&.//), (&/), (&//))
 
 import qualified Control.Monad.Trans.Resource as C
+import           Conduit                      (foldC, sinkList, yieldMany)
 import           Data.Conduit                 ((.|), runConduit,
                                                runConduitRes, ConduitT)
 import           Data.Conduit.Attoparsec      (ParseError(..))
@@ -60,6 +61,8 @@
         it "normalizes line endings" crlfToLfConversion
         it "normalizes \\r at the end of a content" crlfToLfConversionCrAtEnd
         it "normalizes multiple \\rs and \\r\\ns" crlfToLfConversionCrCrCr
+        context "generates events for rendering in a stream" streamRenderGenerateEvents
+        it "renders events from a stream" streamRender
     describe "XML Cursors" $ do
         it "has correct parent" cursorParent
         it "has correct ancestor" cursorAncestor
@@ -1108,3 +1111,47 @@
     where
         doc = D.parseLBS_ def "<crlf>\r\r\r\n\r\r\r</crlf>"
         content = [ContentText "\n\n\n\n\n\n"]
+
+streamRenderGenerateEvents :: Spec
+streamRenderGenerateEvents = do
+    it "generates events for a document" $ do
+        emptyDoc <- runConduit $ R.document mempty .| sinkList
+        emptyDoc @?= [EventBeginDocument, EventEndDocument]
+        nonEmptyDoc <- runConduit $
+            R.document (R.tag "foo" mempty $ R.content "...") .| sinkList
+        nonEmptyDoc @?=
+            [ EventBeginDocument
+            , EventBeginElement "foo" []
+            , EventContent $ ContentText "..."
+            , EventEndElement "foo"
+            , EventEndDocument
+            ]
+    it "generates events for a tag" $ do
+        emptyTag <- runConduit $ R.tag "foo" mempty mempty .| sinkList
+        emptyTag @?= [EventBeginElement "foo" [], EventEndElement "foo"]
+        nonEmptyTag <- runConduit $
+            R.tag "foo" (R.attr "bar" "baz") (R.content "...") .| sinkList
+        nonEmptyTag @?=
+            [ EventBeginElement "foo" [("bar", [ContentText "baz"])]
+            , EventContent $ ContentText "..."
+            , EventEndElement "foo"
+            ]
+
+streamRender :: Assertion
+streamRender = do
+    x <- runConduit $ input .| R.renderBytes def .| foldC
+    x @?= output
+  where
+    input = yieldMany
+        [ EventBeginDocument
+        , EventBeginElement "foo" [("bar", [ContentText "baz"])]
+        , EventContent $ ContentText "..."
+        , EventEndElement "foo"
+        , EventEndDocument
+        ]
+    output = S.concat
+        [ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+        , "<foo bar=\"baz\">"
+        , "..."
+        , "</foo>"
+        ]
diff --git a/xml-conduit.cabal b/xml-conduit.cabal
--- a/xml-conduit.cabal
+++ b/xml-conduit.cabal
@@ -1,7 +1,7 @@
 cabal-version:   1.14
 
 name:            xml-conduit
-version:         1.10.0.1
+version:         1.10.1.0
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>, Aristid Breitkreuz <aristidb@googlemail.com>
@@ -14,7 +14,7 @@
 homepage:        http://github.com/snoyberg/xml
 extra-source-files: README.md
                     ChangeLog.md
-tested-with:     GHC >=8.0 && <9.10
+tested-with:     GHC >=8.0 && <9.12
 
 custom-setup
     setup-depends:   base >= 4 && <5, Cabal <4, cabal-doctest >= 1 && <1.1
