diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for byteslice
 
+## 0.2.13.0 -- 2024-01-12
+
+* Add `replicate` and `replicateByte` to `Data.Bytes.Chunks`.
+
 ## 0.2.12.0 -- 2023-12-14
 
 * Add `Data.Bytes.Chunks.cons`.
diff --git a/byteslice.cabal b/byteslice.cabal
--- a/byteslice.cabal
+++ b/byteslice.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.4
 name: byteslice
-version: 0.2.12.0
+version: 0.2.13.0
 synopsis: Slicing managed and unmanaged memory
 description:
   This library provides types that allow the user to talk about a slice of
diff --git a/src/Data/Bytes.hs b/src/Data/Bytes.hs
--- a/src/Data/Bytes.hs
+++ b/src/Data/Bytes.hs
@@ -38,12 +38,12 @@
   , singleton
   , doubleton
   , tripleton
-  , replicate
+  , Pure.replicate
     -- ** Unsliced
   , singletonU
   , doubletonU
   , tripletonU
-  , replicateU
+  , Pure.replicateU
     -- * Filtering
   , takeWhile
   , dropWhile
@@ -312,20 +312,6 @@
   PM.writeByteArray arr 0 a
   PM.writeByteArray arr 1 b
   PM.writeByteArray arr 2 c
-  PM.unsafeFreezeByteArray arr
-
--- | Replicate a byte @n@ times.
-replicate ::
-     Int -- ^ Desired length @n@
-  -> Word8 -- ^ Byte to replicate
-  -> Bytes
-replicate !n !w = Bytes (replicateU n w) 0 n
-
--- | Variant of 'replicate' that returns a unsliced byte array.
-replicateU :: Int -> Word8 -> ByteArray
-replicateU !n !w = runByteArrayST do
-  arr <- PM.newByteArray n
-  PM.setByteArray arr 0 n w
   PM.unsafeFreezeByteArray arr
 
 -- | /O(n)/ Return the suffix of the second string if its prefix
diff --git a/src/Data/Bytes/Chunks.hs b/src/Data/Bytes/Chunks.hs
--- a/src/Data/Bytes/Chunks.hs
+++ b/src/Data/Bytes/Chunks.hs
@@ -29,6 +29,8 @@
   , concatByteString
   , reverse
   , reverseOnto
+  , replicate
+  , replicateByte
     -- * Folds
   , foldl'
     -- * Splitting
@@ -48,7 +50,7 @@
   , writeFile
   ) where
 
-import Prelude hiding (Foldable(..),concat,reverse,readFile,writeFile)
+import Prelude hiding (Foldable(..),concat,reverse,readFile,writeFile,replicate)
 
 import Control.Exception (IOException,catch)
 import Control.Monad.ST.Run (runIntByteArrayST)
@@ -94,6 +96,50 @@
 cons :: Bytes -> Chunks -> Chunks
 {-# inline cons #-}
 cons = ChunksCons
+
+-- | Repeat the byte sequence over and over. Returns empty chunks when given
+-- a negative repetition count.
+replicate ::
+     Bytes
+  -> Int -- ^ Number of times to repeat the sequence.
+  -> Chunks
+replicate !b@(Bytes _ _ len) !n
+  | n <= 0 = ChunksNil
+  | len == 0 = ChunksNil
+  | otherwise = go n ChunksNil
+  where
+  -- Implementation note: We do not have to reverse the chunks at the end.
+  go i !acc = case i of
+    0 -> acc
+    _ -> go (i - 1) (ChunksCons b acc)
+
+-- | Repeat the byte over and over. This builds a single byte array that
+-- is at most 64KiB and shares that across every @ChunksCons@ cell.
+--
+-- An as example, creating a 2GiB chunks this way would use 64KiB for the
+-- byte array, and there would be the additional overhead of the 2^15
+-- @ChunksCons@ data constructors. On a 64-bit platform, @ChunksCons@
+-- takes 40 bytes, so the total memory consumption would be
+-- @2^16 + 40 * 2^15@, which is roughly 1.37MB. The same reasoning
+-- shows that it takes about 83.95MB to represent a 128GiB chunks.
+--
+-- The size of the shared payload is an implementation detail. Do not
+-- rely on this function producing 64KiB chunks. The implementation might
+-- one day change to something smarter that minimizes the memory footprint
+-- for very large chunks.
+replicateByte ::
+     Word8
+  -> Int -- ^ Number of times to replicate the byte
+  -> Chunks
+replicateByte !w !n
+  | n <= 0 = ChunksNil
+  | n < 65536 = ChunksCons (Bytes.replicate n w) ChunksNil
+  | otherwise = go (Bytes.replicateU 65536 w) n ChunksNil
+  where
+  go !shared !remaining !acc
+    | remaining == 0 = acc
+    | remaining < 65536 = ChunksCons (Bytes shared 0 remaining) acc
+    | otherwise = go shared (remaining - 65536) (ChunksCons (Bytes shared 0 65536) acc)
 
 -- | Are there any bytes in the chunked byte sequences?
 null :: Chunks -> Bool
diff --git a/src/Data/Bytes/Pure.hs b/src/Data/Bytes/Pure.hs
--- a/src/Data/Bytes/Pure.hs
+++ b/src/Data/Bytes/Pure.hs
@@ -40,9 +40,11 @@
   , mapU
   , null
   , toShortByteString
+  , replicate
+  , replicateU
   ) where
 
-import Prelude hiding (Foldable(..),map)
+import Prelude hiding (Foldable(..),map,replicate)
 
 import Control.Monad.Primitive (PrimState,PrimMonad)
 import Control.Monad.ST.Run (runByteArrayST)
@@ -346,3 +348,16 @@
 toShortByteString !b = case toByteArray b of
   PM.ByteArray x -> SBS x
 
+-- | Replicate a byte @n@ times.
+replicate ::
+     Int -- ^ Desired length @n@
+  -> Word8 -- ^ Byte to replicate
+  -> Bytes
+replicate !n !w = Bytes (replicateU n w) 0 n
+
+-- | Variant of 'replicate' that returns a unsliced byte array.
+replicateU :: Int -> Word8 -> ByteArray
+replicateU !n !w = runByteArrayST do
+  arr <- PM.newByteArray n
+  PM.setByteArray arr 0 n w
+  PM.unsafeFreezeByteArray arr
