diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Revision history for base16
 
+## 0.3.2.1
+
+* Add support for GHC 9.4
+* Added `decodeBase16'` to `Data.ByteString.Base16*` (thanks to @shlevy [#20](https://github.com/emilypi/Base16/pull/20))
 ## 0.3.2.0
 
 * Fix incorrect behavior in `Data.ByteString.Short.Base16#encodeBase16`
diff --git a/base16.cabal b/base16.cabal
--- a/base16.cabal
+++ b/base16.cabal
@@ -1,6 +1,6 @@
-cabal-version:   2.0
+cabal-version:   3.0
 name:            base16
-version:         0.3.2.0
+version:         0.3.2.1
 synopsis:        Fast RFC 4648-compliant Base16 encoding
 description:
   RFC 4648-compliant Base16 encodings and decodings.
@@ -8,7 +8,7 @@
 
 homepage:        https://github.com/emilypi/base16
 bug-reports:     https://github.com/emilypi/base16/issues
-license:         BSD3
+license:         BSD-3-Clause
 license-file:    LICENSE
 author:          Emily Pillmore
 maintainer:      emilypi@cohomolo.gy
@@ -21,8 +21,9 @@
 
 tested-with:
   GHC ==8.10.7
-   || ==9.0.1
-   || ==9.2.1
+   || ==9.0.2
+   || ==9.2.5
+   || ==9.4.3
 
 source-repository head
   type:     git
@@ -45,11 +46,11 @@
     Data.ByteString.Base16.Internal.W16.ShortLoop
 
   build-depends:
-      base        >=4.14 && <4.17
+      base        >=4.14 && <4.18
     , bytestring  >=0.10 && <0.12
     , deepseq     ^>=1.4
     , primitive   >=0.6  && <0.8
-    , text        ^>=1.2
+    , text        ^>=1.2 || ^>= 2.0
     , text-short  ^>=0.1
 
   hs-source-dirs:   src
@@ -63,7 +64,7 @@
   hs-source-dirs:   test
   main-is:          Main.hs
   build-depends:
-      base               >=4.14 && <4.17
+      base               >=4.14 && <4.18
     , base16
     , base16-bytestring  >=1.0
     , bytestring
@@ -81,7 +82,7 @@
   hs-source-dirs:   benchmarks
   main-is:          Base16Bench.hs
   build-depends:
-      base               >=4.14 && <4.17
+      base               >=4.14 && <4.18
     , base16
     , base16-bytestring  >=1.0
     , bytestring
diff --git a/src/Data/ByteString/Base16.hs b/src/Data/ByteString/Base16.hs
--- a/src/Data/ByteString/Base16.hs
+++ b/src/Data/ByteString/Base16.hs
@@ -18,6 +18,7 @@
 ( encodeBase16
 , encodeBase16'
 , decodeBase16
+, decodeBase16'
 , decodeBase16Lenient
 , isBase16
 , isValidBase16
@@ -74,6 +75,22 @@
 decodeBase16 :: ByteString -> Either Text ByteString
 decodeBase16 = decodeBase16_
 {-# INLINE decodeBase16 #-}
+
+-- | Decode Base16 'Text'.
+--
+-- See: <https://tools.ietf.org/html/rfc4648#section-8 RFC-4648 section 8>
+--
+-- === __Examples__:
+--
+-- >>> decodeBase16' "53756e"
+-- Right "Sun"
+--
+-- >>> decodeBase16' "6x"
+-- Left "invalid character at offset: 1"
+--
+decodeBase16' :: Text -> Either Text ByteString
+decodeBase16' = decodeBase16 . T.encodeUtf8
+{-# INLINE decodeBase16' #-}
 
 -- | Decode a Base16-encoded 'ByteString' value leniently, using a
 -- strategy that never fails
diff --git a/src/Data/ByteString/Lazy/Base16.hs b/src/Data/ByteString/Lazy/Base16.hs
--- a/src/Data/ByteString/Lazy/Base16.hs
+++ b/src/Data/ByteString/Lazy/Base16.hs
@@ -18,6 +18,7 @@
 ( encodeBase16
 , encodeBase16'
 , decodeBase16
+, decodeBase16'
 , decodeBase16Lenient
 , isBase16
 , isValidBase16
@@ -81,6 +82,22 @@
 decodeBase16 Empty = Right Empty
 decodeBase16 (Chunk b bs) = Chunk <$> B16.decodeBase16_ b <*> decodeBase16 bs
 {-# INLINE decodeBase16 #-}
+
+-- | Decode Base16 'Text'.
+--
+-- See: <https://tools.ietf.org/html/rfc4648#section-8 RFC-4648 section 8>
+--
+-- === __Examples__:
+--
+-- >>> decodeBase16' "53756e"
+-- Right "Sun"
+--
+-- >>> decodeBase16' "6x"
+-- Left "invalid character at offset: 1"
+--
+decodeBase16' :: Text -> Either T.Text ByteString
+decodeBase16' = decodeBase16 . TL.encodeUtf8
+{-# INLINE decodeBase16' #-}
 
 -- | Decode a Base16-encoded 'ByteString' value leniently, using a
 -- strategy that never fails
diff --git a/src/Data/ByteString/Short/Base16.hs b/src/Data/ByteString/Short/Base16.hs
--- a/src/Data/ByteString/Short/Base16.hs
+++ b/src/Data/ByteString/Short/Base16.hs
@@ -18,17 +18,18 @@
 ( encodeBase16
 , encodeBase16'
 , decodeBase16
+, decodeBase16'
 , decodeBase16Lenient
 , isBase16
 , isValidBase16
 ) where
 
 
-import Data.ByteString.Short (ShortByteString, fromShort)
+import Data.ByteString.Short
 import Data.ByteString.Base16.Internal.Head
 import qualified Data.ByteString.Base16 as B16
 import Data.Text (Text)
-import Data.Text.Short (ShortText)
+import Data.Text.Short
 import Data.Text.Short.Unsafe
 
 
@@ -75,6 +76,22 @@
 decodeBase16 :: ShortByteString -> Either Text ShortByteString
 decodeBase16 = decodeBase16Short_
 {-# INLINE decodeBase16 #-}
+
+-- | Decode Base16 'Text'.
+--
+-- See: <https://tools.ietf.org/html/rfc4648#section-8 RFC-4648 section 8>
+--
+-- === __Examples__:
+--
+-- >>> decodeBase16' "53756e"
+-- Right "Sun"
+--
+-- >>> decodeBase16' "6x"
+-- Left "invalid character at offset: 1"
+--
+decodeBase16' :: ShortText -> Either Text ShortByteString
+decodeBase16' = decodeBase16 . toShortByteString
+{-# INLINE decodeBase16' #-}
 
 -- | Decode a Base16-encoded 'ShortByteString' value leniently, using a
 -- strategy that never fails
