diff --git a/bench/Main.hs b/bench/Main.hs
--- a/bench/Main.hs
+++ b/bench/Main.hs
@@ -17,7 +17,8 @@
     ]
   where
     features =
-      [ bench "text" $ whnf (A.toText . A.text) "фывапролдж",
+      [ bench "string" $ whnf (A.toText . A.string) "фывапролдж",
+        bench "text" $ whnf (A.toText . A.text) "фывапролдж",
         bench "lazyText" $ whnf (A.toText . A.lazyText) "фывапролдж",
         bench "char" $ whnf (A.toText . A.char) 'ф',
         bench "unicodeCodepoint" $ whnf (A.toText . A.unicodeCodepoint) 1092
diff --git a/library/TextBuilderCore.hs b/library/TextBuilderCore.hs
--- a/library/TextBuilderCore.hs
+++ b/library/TextBuilderCore.hs
@@ -11,6 +11,7 @@
     -- * Constructors
 
     -- ** Text
+    string,
     text,
     lazyText,
 
@@ -19,6 +20,7 @@
     unicodeCodepoint,
 
     -- ** Primitives
+    unsafeChars,
     unsafeSeptets,
     unsafeReverseSeptets,
   )
@@ -65,7 +67,7 @@
       (forall s. TextArray.MArray s -> Int -> ST s Int)
 
 instance IsString TextBuilder where
-  fromString = text . fromString
+  fromString = string
 
 instance Show TextBuilder where
   show = show . toText
@@ -132,6 +134,12 @@
 
 -- * Constructors
 
+-- | Construct from a list of characters.
+{-# INLINE string #-}
+string :: String -> TextBuilder
+string string =
+  unsafeChars (length string) string
+
 -- | Strict text.
 {-# INLINEABLE text #-}
 text :: Text -> TextBuilder
@@ -279,6 +287,81 @@
 #endif
 
 -- * Basic Unsafe Primitives
+
+-- |
+-- Helper for constructing from char producers a bit more efficiently than via @(text . fromString)@.
+--
+-- >>> unsafeChars 3 "123"
+-- "123"
+--
+-- >>> unsafeChars 4 "123"
+-- "123"
+{-# INLINE unsafeChars #-}
+unsafeChars ::
+  -- | Maximum size of the provided list of characters.
+  --
+  -- __Warning__: Must be greater than or equal to the length of the list.
+  Int ->
+  [Char] ->
+  TextBuilder
+#if MIN_VERSION_text(2,0,0)
+unsafeChars maxChars chars =
+  TextBuilder
+    (maxChars * 4)
+    ( \array ->
+        foldr
+          ( \char next offset ->
+              Utf8View.unicodeCodepoint
+                (ord char)
+                ( \byte -> do
+                    TextArray.unsafeWrite array offset byte
+                    next (succ offset)
+                )
+                ( \byte1 byte2 -> do
+                    TextArray.unsafeWrite array offset byte1
+                    TextArray.unsafeWrite array (succ offset) byte2
+                    next (offset + 2)
+                )
+                ( \byte1 byte2 byte3 -> do
+                    TextArray.unsafeWrite array offset byte1
+                    TextArray.unsafeWrite array (succ offset) byte2
+                    TextArray.unsafeWrite array (offset + 2) byte3
+                    next (offset + 3)
+                )
+                ( \byte1 byte2 byte3 byte4 -> do
+                    TextArray.unsafeWrite array offset byte1
+                    TextArray.unsafeWrite array (succ offset) byte2
+                    TextArray.unsafeWrite array (offset + 2) byte3
+                    TextArray.unsafeWrite array (offset + 3) byte4
+                    next (offset + 4)
+                )
+          )
+          return
+          chars
+    )
+#else
+unsafeChars maxChars chars =
+  TextBuilder
+    (maxChars * 2)
+    ( \array ->
+        foldr
+          ( \char next offset ->
+              Utf16View.unicodeCodepoint
+                (ord char)
+                ( \byte -> do
+                    TextArray.unsafeWrite array offset byte
+                    next (succ offset)
+                )
+                ( \byte1 byte2 -> do
+                    TextArray.unsafeWrite array offset byte1
+                    TextArray.unsafeWrite array (succ offset) byte2
+                    next (offset + 2)
+                )
+          )
+          return
+          chars
+    )
+#endif
 
 -- |
 -- Provides a unified way to deal with the byte array regardless of the version of the @text@ library.
diff --git a/test/Features.hs b/test/Features.hs
--- a/test/Features.hs
+++ b/test/Features.hs
@@ -3,6 +3,7 @@
 import Control.Monad
 import Data.String
 import qualified Data.Text as Text
+import qualified Data.Text.Lazy as Text.Lazy
 import Test.QuickCheck.Instances ()
 import Test.Tasty
 import Test.Tasty.QuickCheck
@@ -23,13 +24,24 @@
           isEmpty (text a) === Text.null a
       ],
     testGroup "toText" $
-      [ mapsToMonoid toText
+      [ mapsToMonoid toText,
+        testProperty "Roundtrips" \textValue ->
+          toText (text textValue) === textValue
       ],
+    testGroup "string" $
+      [ mapsToMonoid string,
+        testProperty "Roundtrips" \builder ->
+          string (Text.unpack (toText builder)) === builder
+      ],
     testGroup "text" $
-      [ mapsToMonoid text
+      [ mapsToMonoid text,
+        testProperty "Roundtrips" \builder ->
+          text (toText builder) === builder
       ],
     testGroup "lazyText" $
-      [ mapsToMonoid lazyText
+      [ mapsToMonoid lazyText,
+        testProperty "Roundtrips" \builder ->
+          lazyText (Text.Lazy.fromStrict (toText builder)) === builder
       ],
     testGroup "char" $
       [ mapsToMonoid char
diff --git a/text-builder-core.cabal b/text-builder-core.cabal
--- a/text-builder-core.cabal
+++ b/text-builder-core.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: text-builder-core
-version: 0.1.0.1
+version: 0.1.1
 category: Text, Builders
 synopsis: Internals of "text-builder"
 description:
