diff --git a/iri.cabal b/iri.cabal
--- a/iri.cabal
+++ b/iri.cabal
@@ -1,34 +1,31 @@
 cabal-version: 3.0
-name:          iri
-version:       0.5.1
-category:      URI, URL, IRI, Network
-synopsis:      RFC-based resource identifier library
+name: iri
+version: 0.5.1.1
+category: URI, URL, IRI, Network
+synopsis: RFC-compliant universal resource identifier library (URL, URI, IRI)
 description:
   This library provides a universal data model for representation of URI, URL and IRI.
   It comes packed with parsing, rendering and serialisation functionality.
   It is implemented in conformance with the RFC-3986 and RFC-3987 specifications and
   is intended as a standard tool for all tasks related to resource identifiers.
 
-homepage:      https://github.com/nikita-volkov/iri
-bug-reports:   https://github.com/nikita-volkov/iri/issues
-author:        Nikita Volkov <nikita.y.volkov@mail.ru>
-maintainer:    Nikita Volkov <nikita.y.volkov@mail.ru>
-copyright:     (c) 2017 Nikita Volkov
-license:       MIT
-license-file:  LICENSE
+homepage: https://github.com/nikita-volkov/iri
+bug-reports: https://github.com/nikita-volkov/iri/issues
+author: Nikita Volkov <nikita.y.volkov@mail.ru>
+maintainer: Nikita Volkov <nikita.y.volkov@mail.ru>
+copyright: (c) 2017 Nikita Volkov
+license: MIT
+license-file: LICENSE
 
 source-repository head
-  type:     git
-  location: git://github.com/nikita-volkov/iri.git
+  type: git
+  location: https://github.com/nikita-volkov/iri
 
-library
-  hs-source-dirs:     library
-  default-language:   Haskell2010
+common base
+  default-language: Haskell2010
   default-extensions:
-    NoImplicitPrelude
-    NoMonomorphismRestriction
-    Arrows
     BangPatterns
+    BlockArguments
     ConstraintKinds
     DataKinds
     DefaultSignatures
@@ -37,33 +34,56 @@
     DeriveFunctor
     DeriveGeneric
     DeriveTraversable
+    DerivingStrategies
     EmptyDataDecls
     FlexibleContexts
     FlexibleInstances
     FunctionalDependencies
     GADTs
     GeneralizedNewtypeDeriving
-    ImportQualifiedPost
     LambdaCase
     LiberalTypeSynonyms
     MagicHash
     MultiParamTypeClasses
     MultiWayIf
+    NoImplicitPrelude
+    NoMonomorphismRestriction
+    NumericUnderscores
     OverloadedStrings
     ParallelListComp
     PatternGuards
-    PatternSynonyms
     QuasiQuotes
     RankNTypes
     RecordWildCards
     ScopedTypeVariables
     StandaloneDeriving
+    StrictData
     TemplateHaskell
     TupleSections
+    TypeApplications
     TypeFamilies
     TypeOperators
     UnboxedTuples
+    ViewPatterns
 
+common executable
+  import: base
+  ghc-options:
+    -O2
+    -threaded
+    -with-rtsopts=-N
+    -rtsopts
+    -funbox-strict-fields
+
+common test
+  import: base
+  ghc-options:
+    -threaded
+    -with-rtsopts=-N
+
+library
+  import: base
+  hs-source-dirs: library
   exposed-modules:
     Iri.Data
     Iri.Optics
@@ -101,89 +121,49 @@
     Iri.Vector
 
   build-depends:
-    , attoparsec >=0.13 && <0.15
-    , base >=4.15 && <5
-    , bytestring >=0.10 && <0.13
-    , contravariant >=1.4 && <2
-    , hashable >=1.2 && <2
-    , ip >=1.7.8 && <2
-    , iri:punycode
-    , profunctors >=5.2 && <6
-    , ptr >=0.16.1 && <0.17
-    , template-haskell >=2.17 && <3
-    , text >=2 && <3
-    , text-builder >=0.6.7.2 && <0.7
-    , th-lift >=0.8 && <0.9
-    , th-lift-instances >=0.1.20 && <0.2
-    , unordered-containers >=0.2 && <0.3
-    , vector >=0.13 && <0.14
-    , vector-builder >=0.3.4 && <0.4
-    , vector-instances >=3.4 && <4
-    , wide-word >=0.1.6 && <2
+    attoparsec >=0.13 && <0.15,
+    base >=4.15 && <5,
+    bytestring >=0.10 && <0.13,
+    contravariant >=1.4 && <2,
+    hashable >=1.2 && <2,
+    ip >=1.7.8 && <2,
+    iri:punycode,
+    profunctors >=5.2 && <6,
+    ptr >=0.16.1 && <0.17,
+    template-haskell >=2.17 && <3,
+    text >=2 && <3,
+    text-builder ^>=1,
+    th-lift >=0.8 && <0.9,
+    th-lift-instances >=0.1.20 && <0.2,
+    unordered-containers >=0.2 && <0.3,
+    vector >=0.13 && <0.14,
+    vector-builder >=0.3.4 && <0.4,
+    vector-instances >=3.4 && <4,
+    wide-word >=0.1.6 && <2,
 
 library punycode
-  hs-source-dirs:   punycode
+  hs-source-dirs: punycode
   default-language: Haskell2010
   build-depends:
-    , base >=4.15 && <5
-    , bytestring >=0.10 && <0.13
-    , cereal >=0.5.8.3 && <0.6
-    , mtl >=2 && <3
-    , text >=2 && <3
+    base >=4.13 && <5,
+    bytestring >=0.10 && <0.13,
+    cereal >=0.5.8.3 && <0.6,
+    mtl >=2 && <3,
+    text >=2 && <3,
 
-  exposed-modules:  Data.Text.Punycode
+  exposed-modules: Data.Text.Punycode
   other-modules:
     Data.Text.Punycode.Decode
     Data.Text.Punycode.Encode
     Data.Text.Punycode.Shared
 
 test-suite test
-  type:               exitcode-stdio-1.0
-  hs-source-dirs:     test
-  main-is:            Main.hs
-  default-language:   Haskell2010
-  default-extensions:
-    NoImplicitPrelude
-    NoMonomorphismRestriction
-    Arrows
-    BangPatterns
-    ConstraintKinds
-    DataKinds
-    DefaultSignatures
-    DeriveDataTypeable
-    DeriveFoldable
-    DeriveFunctor
-    DeriveGeneric
-    DeriveTraversable
-    EmptyDataDecls
-    FlexibleContexts
-    FlexibleInstances
-    FunctionalDependencies
-    GADTs
-    GeneralizedNewtypeDeriving
-    ImportQualifiedPost
-    LambdaCase
-    LiberalTypeSynonyms
-    MagicHash
-    MultiParamTypeClasses
-    MultiWayIf
-    OverloadedStrings
-    ParallelListComp
-    PatternGuards
-    PatternSynonyms
-    QuasiQuotes
-    RankNTypes
-    RecordWildCards
-    ScopedTypeVariables
-    StandaloneDeriving
-    TemplateHaskell
-    TupleSections
-    TypeFamilies
-    TypeOperators
-    UnboxedTuples
-
+  import: test
+  type: exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is: Main.hs
   build-depends:
-    , iri
-    , rerebase <2
-    , tasty >=0.12 && <2
-    , tasty-hunit >=0.9 && <0.11
+    iri,
+    rerebase <2,
+    tasty >=0.12 && <2,
+    tasty-hunit >=0.9 && <0.11,
diff --git a/library/Iri/CodePointPredicates/Core.hs b/library/Iri/CodePointPredicates/Core.hs
--- a/library/Iri/CodePointPredicates/Core.hs
+++ b/library/Iri/CodePointPredicates/Core.hs
@@ -1,6 +1,6 @@
 module Iri.CodePointPredicates.Core where
 
-import Data.Vector.Unboxed qualified as A
+import qualified Data.Vector.Unboxed as A
 import Iri.Prelude hiding (Predicate, inRange, (&&&), (|||))
 
 type Predicate =
diff --git a/library/Iri/CodePointPredicates/Rfc3987.hs b/library/Iri/CodePointPredicates/Rfc3987.hs
--- a/library/Iri/CodePointPredicates/Rfc3987.hs
+++ b/library/Iri/CodePointPredicates/Rfc3987.hs
@@ -114,7 +114,7 @@
 module Iri.CodePointPredicates.Rfc3987 where
 
 import Iri.CodePointPredicates.Core
-import Iri.CodePointPredicates.Rfc3986 qualified as A
+import qualified Iri.CodePointPredicates.Rfc3986 as A
 import Iri.Prelude hiding (Predicate, inRange, (&&&), (|||))
 
 scheme :: Predicate
diff --git a/library/Iri/Data/Functions.hs b/library/Iri/Data/Functions.hs
--- a/library/Iri/Data/Functions.hs
+++ b/library/Iri/Data/Functions.hs
@@ -14,7 +14,7 @@
     case hierarchy of
       AuthorisedHierarchy (Authority userInfo host port) path -> case userInfo of
         MissingUserInfo -> Right (HttpIri security host port path query fragment)
-        PresentUserInfo (User user) _ -> Left ("User Info is present")
+        PresentUserInfo (User _) _ -> Left ("User Info is present")
       _ -> Left ("Not an authorised hierarchy")
 
 -- | Generalize an HTTP IRI to IRI
diff --git a/library/Iri/Data/Instances/Lift.hs b/library/Iri/Data/Instances/Lift.hs
--- a/library/Iri/Data/Instances/Lift.hs
+++ b/library/Iri/Data/Instances/Lift.hs
@@ -2,12 +2,12 @@
 
 module Iri.Data.Instances.Lift where
 
-import Data.WideWord.Word128 qualified
+import qualified Data.WideWord.Word128
 import Iri.Data.Types
 import Iri.Prelude
 import Language.Haskell.TH.Lift
 import Language.Haskell.TH.Syntax
-import Net.IPv6 qualified
+import qualified Net.IPv6
 
 fmap concat
   $ traverse deriveLift
diff --git a/library/Iri/Data/Instances/Show.hs b/library/Iri/Data/Instances/Show.hs
--- a/library/Iri/Data/Instances/Show.hs
+++ b/library/Iri/Data/Instances/Show.hs
@@ -2,10 +2,10 @@
 
 module Iri.Data.Instances.Show where
 
-import Data.Text qualified as B
+import qualified Data.Text as B
 import Iri.Data.Types
 import Iri.Prelude
-import Iri.Rendering.Text.Internal qualified as A
+import qualified Iri.Rendering.Text.Internal as A
 
 instance Show Iri where
   show =
diff --git a/library/Iri/MonadPlus.hs b/library/Iri/MonadPlus.hs
--- a/library/Iri/MonadPlus.hs
+++ b/library/Iri/MonadPlus.hs
@@ -1,8 +1,8 @@
 module Iri.MonadPlus where
 
 import Iri.Prelude hiding (foldl)
-import Ptr.ByteString qualified as ByteString
-import Ptr.Poking qualified as Poking
+import qualified Ptr.ByteString as ByteString
+import qualified Ptr.Poking as Poking
 
 {-# INLINE foldl #-}
 foldl :: (MonadPlus m) => (a -> b -> a) -> a -> m b -> m a
diff --git a/library/Iri/Optics/Defs.hs b/library/Iri/Optics/Defs.hs
--- a/library/Iri/Optics/Defs.hs
+++ b/library/Iri/Optics/Defs.hs
@@ -1,13 +1,13 @@
 module Iri.Optics.Defs where
 
-import Data.Text.Encoding qualified as Text
+import qualified Data.Text.Encoding as Text
 import Iri.Data
 import Iri.Optics.Basics
-import Iri.Parsing.ByteString qualified as B
-import Iri.Parsing.Text qualified as D
+import qualified Iri.Parsing.ByteString as B
+import qualified Iri.Parsing.Text as D
 import Iri.Prelude
-import Iri.Rendering.ByteString qualified as A
-import Iri.Rendering.Text qualified as C
+import qualified Iri.Rendering.ByteString as A
+import qualified Iri.Rendering.Text as C
 
 -- * Definitions by source
 
diff --git a/library/Iri/Parsing/Attoparsec/ByteString.hs b/library/Iri/Parsing/Attoparsec/ByteString.hs
--- a/library/Iri/Parsing/Attoparsec/ByteString.hs
+++ b/library/Iri/Parsing/Attoparsec/ByteString.hs
@@ -8,21 +8,21 @@
 where
 
 import Data.Attoparsec.ByteString hiding (try)
-import Data.Attoparsec.ByteString.Char8 qualified as F
-import Data.ByteString qualified as K
-import Data.Text.Encoding qualified as B
-import Data.Text.Encoding.Error qualified as L
-import Data.Text.Punycode qualified as A
-import Data.Vector qualified as S
-import Iri.CodePointPredicates.Rfc3986 qualified as C
+import qualified Data.Attoparsec.ByteString.Char8 as F
+import qualified Data.ByteString as K
+import qualified Data.Text.Encoding as B
+import qualified Data.Text.Encoding.Error as L
+import qualified Data.Text.Punycode as A
+import qualified Data.Vector as S
+import qualified Iri.CodePointPredicates.Rfc3986 as C
 import Iri.Data
-import Iri.MonadPlus qualified as R
-import Iri.PercentEncoding qualified as I
+import qualified Iri.MonadPlus as R
+import qualified Iri.PercentEncoding as I
 import Iri.Prelude hiding (foldl, hash)
-import Net.IPv4 qualified as M
-import Net.IPv6 qualified as N
-import Text.Builder qualified as J
-import VectorBuilder.MonadPlus qualified as E
+import qualified Net.IPv4 as M
+import qualified Net.IPv6 as N
+import qualified TextBuilder as J
+import qualified VectorBuilder.MonadPlus as E
 
 {-# INLINE percent #-}
 percent :: Parser Word8
@@ -87,7 +87,7 @@
 uri =
   labeled "URI" $ do
     parsedScheme <- scheme
-    colon
+    _ <- colon
     parsedHierarchy <- hierarchy
     parsedQuery <- query
     parsedFragment <- fragment
@@ -99,12 +99,12 @@
 httpUri :: Parser HttpIri
 httpUri =
   labeled "HTTP URI" $ do
-    satisfy (\x -> x == 104 || x == 72)
-    satisfy (\x -> x == 116 || x == 84)
-    satisfy (\x -> x == 116 || x == 84)
-    satisfy (\x -> x == 112 || x == 80)
+    _ <- satisfy (\x -> x == 104 || x == 72)
+    _ <- satisfy (\x -> x == 116 || x == 84)
+    _ <- satisfy (\x -> x == 116 || x == 84)
+    _ <- satisfy (\x -> x == 112 || x == 80)
     secure <- satisfy (\b -> b == 115 || b == 83) $> True <|> pure False
-    string "://"
+    _ <- string "://"
     parsedHost <- host
     parsedPort <- PresentPort <$> (colon *> port) <|> pure MissingPort
     parsedPath <- (forwardSlash *> path) <|> pure (Path mempty)
@@ -169,26 +169,26 @@
 ipV6 =
   do
     a <- F.hexadecimal
-    colon
+    _ <- colon
     b <- F.hexadecimal
-    colon
+    _ <- colon
     c <- F.hexadecimal
-    colon
+    _ <- colon
     d <- F.hexadecimal
-    colon
+    _ <- colon
     mplus
       ( do
           e <- F.hexadecimal
-          colon
+          _ <- colon
           f <- F.hexadecimal
-          colon
+          _ <- colon
           g <- F.hexadecimal
-          colon
+          _ <- colon
           h <- F.hexadecimal
           return (N.fromWord16s a b c d e f g h)
       )
       ( do
-          colon
+          _ <- colon
           return (N.fromWord16s a b c d 0 0 0 0)
       )
 
@@ -252,7 +252,7 @@
           fail "Unexpected decoding error"
     finish (builder, undecodedBytes, _) =
       if K.null undecodedBytes
-        then return (J.run builder)
+        then return (J.toText builder)
         else fail (showString "UTF8 decoding: Bytes remaining: " (show undecodedBytes))
 
 {-# INLINEABLE urlEncodedString #-}
@@ -270,7 +270,7 @@
 percentEncodedByte :: Parser Word8
 percentEncodedByte =
   labeled "Percent-encoded byte" $ do
-    percent
+    _ <- percent
     byte1 <- anyWord8
     byte2 <- anyWord8
     I.matchPercentEncodedBytes (fail "Broken percent encoding") return byte1 byte2
diff --git a/library/Iri/Parsing/Attoparsec/Text.hs b/library/Iri/Parsing/Attoparsec/Text.hs
--- a/library/Iri/Parsing/Attoparsec/Text.hs
+++ b/library/Iri/Parsing/Attoparsec/Text.hs
@@ -17,20 +17,20 @@
 where
 
 import Data.Attoparsec.Text hiding (try)
-import Data.ByteString qualified as K
-import Data.Text.Encoding qualified as B
-import Data.Text.Encoding.Error qualified as L
-import Data.Vector qualified as S
-import Iri.CodePointPredicates.Rfc3987 qualified as C
+import qualified Data.ByteString as K
+import qualified Data.Text.Encoding as B
+import qualified Data.Text.Encoding.Error as L
+import qualified Data.Vector as S
+import qualified Iri.CodePointPredicates.Rfc3987 as C
 import Iri.Data
-import Iri.MonadPlus qualified as R
+import qualified Iri.MonadPlus as R
 import Iri.Prelude
-import Net.IPv4 qualified as M
-import Net.IPv6 qualified as N
-import Ptr.ByteString qualified as ByteString
-import Ptr.Poking qualified as Poking
-import Text.Builder qualified as J
-import VectorBuilder.MonadPlus qualified as E
+import qualified Net.IPv4 as M
+import qualified Net.IPv6 as N
+import qualified Ptr.ByteString as ByteString
+import qualified Ptr.Poking as Poking
+import qualified TextBuilder as J
+import qualified VectorBuilder.MonadPlus as E
 
 {-# INLINE labeled #-}
 labeled :: String -> Parser a -> Parser a
@@ -45,7 +45,7 @@
 iri =
   labeled "IRI" $ do
     parsedScheme <- scheme
-    char ':'
+    _ <- char ':'
     parsedHierarchy <- hierarchy
     parsedQuery <- query
     parsedFragment <- fragment
@@ -57,9 +57,9 @@
 httpIri :: Parser HttpIri
 httpIri =
   labeled "HTTP IRI" $ do
-    asciiCI "http"
+    _ <- asciiCI "http"
     secure <- satisfy (\x -> x == 's' || x == 'S') $> True <|> pure False
-    string "://"
+    _ <- string "://"
     parsedHost <- host
     parsedPort <- PresentPort <$> (char ':' *> port) <|> pure MissingPort
     parsedPath <- ((char '/') *> path) <|> pure (Path mempty)
@@ -171,13 +171,13 @@
 urlEncodedComponentText :: (Char -> Bool) -> Parser Text
 urlEncodedComponentText unencodedCharPredicate =
   labeled "URL-encoded component"
-    $ fmap J.run
+    $ fmap J.toText
     $ R.foldl mappend mempty
     $ (J.text <$> takeWhile1 unencodedCharPredicate)
     <|> urlEncodedSequenceTextBuilder
 
 {-# INLINEABLE urlEncodedSequenceTextBuilder #-}
-urlEncodedSequenceTextBuilder :: Parser J.Builder
+urlEncodedSequenceTextBuilder :: Parser J.TextBuilder
 urlEncodedSequenceTextBuilder =
   labeled "URL-encoded sequence" $ do
     start <- progress (mempty, mempty, B.streamDecodeUtf8) =<< urlEncodedByte
@@ -200,7 +200,7 @@
 urlEncodedByte :: Parser Word8
 urlEncodedByte =
   do
-    char '%'
+    _ <- char '%'
     digit1 <- fromIntegral <$> hexadecimalDigit
     digit2 <- fromIntegral <$> hexadecimalDigit
     return (shiftL digit1 4 .|. digit2)
diff --git a/library/Iri/Parsing/ByteString.hs b/library/Iri/Parsing/ByteString.hs
--- a/library/Iri/Parsing/ByteString.hs
+++ b/library/Iri/Parsing/ByteString.hs
@@ -8,10 +8,10 @@
   )
 where
 
-import Data.Attoparsec.ByteString qualified as B
-import Data.ByteString qualified as ByteString
+import qualified Data.Attoparsec.ByteString as B
+import qualified Data.ByteString as ByteString
 import Iri.Data
-import Iri.Parsing.Attoparsec.ByteString qualified as A
+import qualified Iri.Parsing.Attoparsec.ByteString as A
 import Iri.Prelude
 
 parser :: B.Parser a -> ByteString -> Either Text a
@@ -43,9 +43,9 @@
 uriQuery :: ByteString -> Either Text Query
 uriQuery x =
   case ByteString.break (== 63) x of
-    (beforeQuestion, questionAndAfterQuestion) -> case ByteString.uncons questionAndAfterQuestion of
+    (_, questionAndAfterQuestion) -> case ByteString.uncons questionAndAfterQuestion of
       Just (_, afterQuestion) -> case ByteString.break (== 35) afterQuestion of
-        (beforeHash, hashAndAfterHash) -> Right (Query beforeHash)
+        (beforeHash, _) -> Right (Query beforeHash)
       Nothing ->
         Left "Does not start with a question mark"
 
diff --git a/library/Iri/Parsing/Text.hs b/library/Iri/Parsing/Text.hs
--- a/library/Iri/Parsing/Text.hs
+++ b/library/Iri/Parsing/Text.hs
@@ -14,9 +14,9 @@
   )
 where
 
-import Data.Attoparsec.Text qualified as B
+import qualified Data.Attoparsec.Text as B
 import Iri.Data
-import Iri.Parsing.Attoparsec.Text qualified as A
+import qualified Iri.Parsing.Attoparsec.Text as A
 import Iri.Prelude
 
 fromParser :: B.Parser a -> Text -> Either Text a
diff --git a/library/Iri/QuasiQuoter.hs b/library/Iri/QuasiQuoter.hs
--- a/library/Iri/QuasiQuoter.hs
+++ b/library/Iri/QuasiQuoter.hs
@@ -6,10 +6,10 @@
   )
 where
 
-import Data.Attoparsec.ByteString qualified as B
-import Data.Attoparsec.Text qualified as C
-import Iri.Parsing.Attoparsec.ByteString qualified as A
-import Iri.Parsing.Attoparsec.Text qualified as D
+import qualified Data.Attoparsec.ByteString as B
+import qualified Data.Attoparsec.Text as C
+import qualified Iri.Parsing.Attoparsec.ByteString as A
+import qualified Iri.Parsing.Attoparsec.Text as D
 import Iri.Prelude hiding (exp)
 import Language.Haskell.TH.Quote
 import Language.Haskell.TH.Syntax
diff --git a/library/Iri/Rendering/ByteString.hs b/library/Iri/Rendering/ByteString.hs
--- a/library/Iri/Rendering/ByteString.hs
+++ b/library/Iri/Rendering/ByteString.hs
@@ -2,8 +2,8 @@
 
 import Iri.Data
 import Iri.Prelude
-import Iri.Rendering.Ptr.Poking qualified as A
-import Ptr.ByteString qualified as F
+import qualified Iri.Rendering.Ptr.Poking as A
+import qualified Ptr.ByteString as F
 
 -- | Render as URI ASCII bytes
 uri :: Iri -> ByteString
diff --git a/library/Iri/Rendering/Ptr/Poking.hs b/library/Iri/Rendering/Ptr/Poking.hs
--- a/library/Iri/Rendering/Ptr/Poking.hs
+++ b/library/Iri/Rendering/Ptr/Poking.hs
@@ -10,19 +10,19 @@
   )
 where
 
-import Data.ByteString qualified as ByteString
-import Data.Text qualified as C
-import Data.Text.Encoding qualified as A
-import Data.Text.Punycode qualified as B
-import Iri.CodePointPredicates.Core qualified as I
-import Iri.CodePointPredicates.Rfc3986 qualified as I
+import qualified Data.ByteString as ByteString
+import qualified Data.Text as C
+import qualified Data.Text.Encoding as A
+import qualified Data.Text.Punycode as B
+import qualified Iri.CodePointPredicates.Core as I
+import qualified Iri.CodePointPredicates.Rfc3986 as I
 import Iri.Data
 import Iri.Prelude hiding (null, poke)
-import Iri.Rendering.Ptr.Poke qualified as L
-import Iri.Utf8CodePoint qualified as K
-import Iri.Vector qualified as F
-import Net.IPv4 qualified as D
-import Net.IPv6 qualified as E
+import qualified Iri.Rendering.Ptr.Poke as L
+import qualified Iri.Utf8CodePoint as K
+import qualified Iri.Vector as F
+import qualified Net.IPv4 as D
+import qualified Net.IPv6 as E
 import Ptr.Poking
 
 uri :: Iri -> Poking
diff --git a/library/Iri/Rendering/Text/Internal.hs b/library/Iri/Rendering/Text/Internal.hs
--- a/library/Iri/Rendering/Text/Internal.hs
+++ b/library/Iri/Rendering/Text/Internal.hs
@@ -2,55 +2,55 @@
 
 import Iri.Data.Types
 import Iri.Prelude
-import Iri.Rendering.TextBuilder.Internal qualified as A
-import Text.Builder qualified as B
+import qualified Iri.Rendering.TextBuilder.Internal as A
+import qualified TextBuilder as B
 
 -- | Render as a Unicode IRI text
 iri :: Iri -> Text
-iri = B.run . A.iri
+iri = B.toText . A.iri
 
 -- | Render as a Unicode IRI text
 httpIri :: HttpIri -> Text
-httpIri = B.run . A.httpIri
+httpIri = B.toText . A.httpIri
 
 scheme :: Scheme -> Text
-scheme = B.run . A.scheme
+scheme = B.toText . A.scheme
 
 hierarchy :: Hierarchy -> Text
-hierarchy = B.run . A.hierarchy
+hierarchy = B.toText . A.hierarchy
 
 authority :: Authority -> Text
-authority = B.run . A.authority
+authority = B.toText . A.authority
 
 userInfo :: UserInfo -> Text
-userInfo = B.run . A.userInfo
+userInfo = B.toText . A.userInfo
 
 host :: Host -> Text
-host = B.run . A.host
+host = B.toText . A.host
 
 regName :: RegName -> Text
-regName = B.run . A.regName
+regName = B.toText . A.regName
 
 domainLabel :: DomainLabel -> Text
-domainLabel = B.run . A.domainLabel
+domainLabel = B.toText . A.domainLabel
 
 ipV4 :: IPv4 -> Text
-ipV4 = B.run . A.ipV4
+ipV4 = B.toText . A.ipV4
 
 ipV6 :: IPv6 -> Text
-ipV6 = B.run . A.ipV6
+ipV6 = B.toText . A.ipV6
 
 port :: Port -> Text
-port = B.run . A.port
+port = B.toText . A.port
 
 path :: Path -> Text
-path = B.run . A.path
+path = B.toText . A.path
 
 pathSegment :: PathSegment -> Text
-pathSegment = B.run . A.pathSegment
+pathSegment = B.toText . A.pathSegment
 
 query :: Query -> Text
-query = B.run . A.query
+query = B.toText . A.query
 
 fragment :: Fragment -> Text
-fragment = B.run . A.fragment
+fragment = B.toText . A.fragment
diff --git a/library/Iri/Rendering/TextBuilder/Internal.hs b/library/Iri/Rendering/TextBuilder/Internal.hs
--- a/library/Iri/Rendering/TextBuilder/Internal.hs
+++ b/library/Iri/Rendering/TextBuilder/Internal.hs
@@ -18,22 +18,22 @@
   )
 where
 
-import Data.ByteString qualified as ByteString
-import Data.Text qualified as C
-import Data.Text.Encoding qualified as A
-import Data.Text.Encoding.Error qualified as A
-import Iri.CodePointPredicates.Core qualified as CorePredicates
-import Iri.CodePointPredicates.Rfc3986 qualified as Rfc3986Predicates
-import Iri.CodePointPredicates.Rfc3987 qualified as Rfc3987Predicates
+import qualified Data.ByteString as ByteString
+import qualified Data.Text as C
+import qualified Data.Text.Encoding as A
+import qualified Data.Text.Encoding.Error as A
+import qualified Iri.CodePointPredicates.Core as CorePredicates
+import qualified Iri.CodePointPredicates.Rfc3986 as Rfc3986Predicates
+import qualified Iri.CodePointPredicates.Rfc3987 as Rfc3987Predicates
 import Iri.Data.Types
 import Iri.Prelude hiding (null)
-import Iri.Utf8CodePoint qualified as K
-import Iri.Vector qualified as F
-import Net.IPv4 qualified as D
-import Net.IPv6 qualified as E
-import Text.Builder
+import qualified Iri.Utf8CodePoint as K
+import qualified Iri.Vector as F
+import qualified Net.IPv4 as D
+import qualified Net.IPv6 as E
+import TextBuilder
 
-iri :: Iri -> Builder
+iri :: Iri -> TextBuilder
 iri (Iri schemeValue hierarchyValue queryValue fragmentValue) =
   scheme schemeValue
     <> char ':'
@@ -47,7 +47,7 @@
            (fragment fragmentValue)
        )
 
-httpIri :: HttpIri -> Builder
+httpIri :: HttpIri -> TextBuilder
 httpIri (HttpIri (Security secure) hostValue portValue pathValue queryValue fragmentValue) =
   (if secure then string "https://" else string "http://")
     <> host hostValue
@@ -56,11 +56,11 @@
     <> prependIfNotNull (char '?') (query queryValue)
     <> prependIfNotNull (char '#') (fragment fragmentValue)
 
-scheme :: Scheme -> Builder
+scheme :: Scheme -> TextBuilder
 scheme (Scheme bytes) =
   text (A.decodeUtf8With A.lenientDecode bytes)
 
-hierarchy :: Hierarchy -> Builder
+hierarchy :: Hierarchy -> TextBuilder
 hierarchy =
   \case
     AuthorisedHierarchy authorityValue pathValue ->
@@ -70,13 +70,13 @@
     RelativeHierarchy pathValue ->
       path pathValue
 
-authority :: Authority -> Builder
+authority :: Authority -> TextBuilder
 authority (Authority userInfoValue hostValue portValue) =
   appendIfNotNull (char '@') (userInfo userInfoValue)
     <> host hostValue
     <> prependIfNotNull (char ':') (port portValue)
 
-userInfo :: UserInfo -> Builder
+userInfo :: UserInfo -> TextBuilder
 userInfo =
   \case
     PresentUserInfo (User user) password -> case password of
@@ -84,82 +84,82 @@
       MissingPassword -> userInfoComponent user
     MissingUserInfo -> mempty
 
-userInfoComponent :: ByteString -> Builder
+userInfoComponent :: ByteString -> TextBuilder
 userInfoComponent =
   urlEncodedBytesOrText Rfc3987Predicates.unencodedUserInfoComponent Rfc3986Predicates.unencodedUserInfoComponent
 
-host :: Host -> Builder
+host :: Host -> TextBuilder
 host =
   \case
     NamedHost value -> regName value
     IpV4Host value -> ipV4 value
     IpV6Host value -> ipV6 value
 
-regName :: RegName -> Builder
+regName :: RegName -> TextBuilder
 regName (RegName vector) =
   F.intercalate domainLabel (char '.') vector
 
-domainLabel :: DomainLabel -> Builder
+domainLabel :: DomainLabel -> TextBuilder
 domainLabel (DomainLabel x) =
   text x
 
-ipV4 :: IPv4 -> Builder
+ipV4 :: IPv4 -> TextBuilder
 ipV4 =
   text . D.encode
 
-ipV6 :: IPv6 -> Builder
+ipV6 :: IPv6 -> TextBuilder
 ipV6 =
   text . E.encode
 
-port :: Port -> Builder
+port :: Port -> TextBuilder
 port =
   \case
-    PresentPort value -> unsignedDecimal value
+    PresentPort value -> decimal value
     MissingPort -> mempty
 
-path :: Path -> Builder
+path :: Path -> TextBuilder
 path (Path pathSegmentVector) =
   F.intercalate pathSegment (char '/') pathSegmentVector
 
-pathSegment :: PathSegment -> Builder
+pathSegment :: PathSegment -> TextBuilder
 pathSegment (PathSegment value) =
   urlEncodedBytesOrText Rfc3987Predicates.unencodedPathSegment Rfc3986Predicates.unencodedPathSegment value
 
-query :: Query -> Builder
+query :: Query -> TextBuilder
 query (Query value) =
   urlEncodedBytesOrText Rfc3987Predicates.unencodedQuery Rfc3986Predicates.unencodedQuery value
 
-fragment :: Fragment -> Builder
+fragment :: Fragment -> TextBuilder
 fragment (Fragment value) =
   urlEncodedBytesOrText Rfc3987Predicates.unencodedFragment Rfc3986Predicates.unencodedFragment value
 
-urlEncodedBytesOrText :: CorePredicates.Predicate -> CorePredicates.Predicate -> ByteString -> Builder
+urlEncodedBytesOrText :: CorePredicates.Predicate -> CorePredicates.Predicate -> ByteString -> TextBuilder
 urlEncodedBytesOrText unencodedPredicate1 unencodedPredicate2 bytes =
   case A.decodeUtf8' bytes of
     Right text -> urlEncodedText unencodedPredicate1 text
     Left _ -> urlEncodedBytes unencodedPredicate2 bytes
 
 -- | Apply URL-encoding to text
-urlEncodedBytes :: CorePredicates.Predicate -> ByteString -> Builder
+urlEncodedBytes :: CorePredicates.Predicate -> ByteString -> TextBuilder
 urlEncodedBytes unencodedPredicate =
   ByteString.foldl'
     ( \builder ->
         mappend builder . \byte ->
           if unencodedPredicate (fromIntegral byte)
-            then utf8CodeUnits1 byte
+            then unicodeCodepoint (fromIntegral byte)
             else urlEncodedByte byte
     )
     mempty
 
 -- | Apply URL-encoding to text
-urlEncodedText :: CorePredicates.Predicate -> Text -> Builder
+urlEncodedText :: CorePredicates.Predicate -> Text -> TextBuilder
 urlEncodedText unencodedPredicate =
   C.foldl' (\builder -> mappend builder . urlEncodedUnicodeCodePoint unencodedPredicate . ord) mempty
 
-urlEncodedUnicodeCodePoint :: CorePredicates.Predicate -> Int -> Builder
+urlEncodedUnicodeCodePoint :: CorePredicates.Predicate -> Int -> TextBuilder
 urlEncodedUnicodeCodePoint unencodedPredicate codePoint =
   if unencodedPredicate codePoint
-    then unicodeCodePoint codePoint
+    then unicodeCodepoint codePoint
     else
       K.unicodeCodePoint
         codePoint
@@ -168,19 +168,17 @@
         (\b1 b2 b3 -> urlEncodedByte b1 <> urlEncodedByte b2 <> urlEncodedByte b3)
         (\b1 b2 b3 b4 -> urlEncodedByte b1 <> urlEncodedByte b2 <> urlEncodedByte b3 <> urlEncodedByte b4)
 
-urlEncodedByte :: Word8 -> Builder
-urlEncodedByte x =
-  case divMod x 16 of
-    (d1, d2) -> char '%' <> hexadecimalDigit d1 <> hexadecimalDigit d2
+urlEncodedByte :: Word8 -> TextBuilder
+urlEncodedByte x = char '%' <> hexadecimal x
 
-prependIfNotNull :: Builder -> Builder -> Builder
+prependIfNotNull :: TextBuilder -> TextBuilder -> TextBuilder
 prependIfNotNull prepended it =
-  if null it
+  if isEmpty it
     then mempty
     else prepended <> it
 
-appendIfNotNull :: Builder -> Builder -> Builder
+appendIfNotNull :: TextBuilder -> TextBuilder -> TextBuilder
 appendIfNotNull appended it =
-  if null it
+  if isEmpty it
     then mempty
     else it <> appended
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,10 +1,10 @@
 module Main where
 
-import Iri.Parsing.ByteString qualified as D
-import Iri.Parsing.Text qualified as F
+import qualified Iri.Parsing.ByteString as D
+import qualified Iri.Parsing.Text as F
 import Iri.QuasiQuoter
-import Iri.Rendering.ByteString qualified as E
-import Iri.Rendering.Text qualified as C
+import qualified Iri.Rendering.ByteString as E
+import qualified Iri.Rendering.Text as C
 import Test.Tasty
 import Test.Tasty.HUnit
 import Prelude
