diff --git a/Data/Text/Template.hs b/Data/Text/Template.hs
new file mode 100644
--- /dev/null
+++ b/Data/Text/Template.hs
@@ -0,0 +1,300 @@
+-- | A simple string substitution library that supports \"$\"-based
+-- substitution. Substitution uses the following rules:
+--
+--    * \"$$\" is an escape; it is replaced with a single \"$\".
+--
+--    * \"$identifier\" names a substitution placeholder matching a
+--      mapping key of \"identifier\". \"identifier\" must spell a
+--      Haskell identifier. The first non-identifier character after the
+--      \"$\" character terminates this placeholder specification.
+--
+--    * \"${identifier}\" is equivalent to \"$identifier\". It is
+--      required when valid identifier characters follow the placeholder
+--      but are not part of the placeholder, such as
+--      \"${noun}ification\".
+--
+-- Any other apperance of \"$\" in the string will result in an
+-- 'Prelude.error' being raised.
+--
+-- If you render the same template multiple times it's faster to first
+-- convert it to a more efficient representation using 'template' and
+-- then render it using 'render'. In fact, all that 'substitute' does
+-- is to combine these two steps.
+
+module Data.Text.Template
+    (
+     -- * The @Template@ type
+     Template,
+
+     -- * The @Context@ type
+     Context,
+     ContextA,
+
+     -- * Basic interface
+     template,
+     render,
+     substitute,
+     showTemplate,
+
+     -- * Applicative interface
+     renderA,
+     substituteA,
+
+     -- * Example
+     -- $example
+    ) where
+
+import Control.Applicative (Applicative(pure), (<$>))
+import Control.Monad (liftM, liftM2)
+import Control.Monad.State (State, evalState, get, put)
+import Data.Char (isAlphaNum)
+import Data.Function (on)
+import Data.Traversable (traverse)
+import Prelude hiding (takeWhile)
+
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as LT
+
+-- -----------------------------------------------------------------------------
+
+-- | A repesentation of a 'Data.Text' template, supporting efficient
+-- rendering.
+newtype Template = Template [Frag]
+
+instance Eq Template where
+    (==) = (==) `on` showTemplate
+
+instance Show Template where
+    show = T.unpack . showTemplate
+
+-- | Shows the template string.
+showTemplate :: Template -> T.Text
+showTemplate (Template fs) = T.concat $ map showFrag fs
+
+-- | A template fragment.
+data Frag = Lit {-# UNPACK #-} !T.Text | Var {-# UNPACK #-} !T.Text !Bool
+
+instance Show Frag where
+    show = T.unpack . showFrag
+
+showFrag :: Frag -> T.Text
+showFrag (Var s b)
+    | b          = T.concat [T.pack "${", s, T.pack "}"]
+    | otherwise  = T.concat [T.pack "$", s]
+showFrag (Lit s) = T.concatMap escape s
+    where escape '$' = T.pack "$$"
+          escape c   = T.singleton c
+
+-- | A mapping from placeholders in the template to values.
+type Context = T.Text -> T.Text
+
+-- | Like 'Context', but with an applicative lookup function.
+type ContextA f = T.Text -> f T.Text
+
+-- -----------------------------------------------------------------------------
+-- Basic interface
+
+-- | Creates a template from a template string.
+template :: T.Text -> Template
+template = runParser pTemplate
+
+-- | Performs the template substitution, returning a new 'LT.Text'.
+render :: Template -> Context -> LT.Text
+render (Template frags) ctxFunc = LT.fromChunks $ map renderFrag frags
+  where
+    renderFrag (Lit s)   = s
+    renderFrag (Var x _) = ctxFunc x
+
+-- | Like 'render', but allows the lookup to have side effects.  The
+-- lookups are performed in order that they are needed to generate the
+-- resulting text.
+--
+-- You can use this e.g. to report errors when a lookup cannot be made
+-- successfully.  For example, given a list @ctx@ of key-value pairs
+-- and a @Template@ @tmpl@:
+--
+-- > renderA tmpl (flip lookup ctx)
+--
+-- will return @Nothing@ if any of the placeholders in the template
+-- don't appear in @ctx@ and @Just text@ otherwise.
+renderA :: Applicative f => Template -> ContextA f -> f LT.Text
+renderA (Template frags) ctxFunc = LT.fromChunks <$> traverse renderFrag frags
+  where
+    renderFrag (Lit s)   = pure s
+    renderFrag (Var x _) = ctxFunc x
+
+-- | Performs the template substitution, returning a new
+-- 'LT.Text'. Note that
+--
+-- > substitute tmpl ctx == render (template tmpl) ctx
+substitute :: T.Text -> Context -> LT.Text
+substitute = render . template
+
+-- | Performs the template substitution in the given @Applicative@,
+-- returning a new 'LT.Text'. Note that
+--
+-- > substituteA tmpl ctx == renderA (template tmpl) ctx
+substituteA :: Applicative f => T.Text -> ContextA f -> f LT.Text
+substituteA = renderA . template
+
+-- -----------------------------------------------------------------------------
+-- Template parser
+
+pTemplate :: Parser Template
+pTemplate = fmap Template pFrags
+
+pFrags :: Parser [Frag]
+pFrags = do
+    c <- peek
+    case c of
+        Nothing  -> return []
+        Just '$' -> do c' <- peekSnd
+                       case c' of
+                           Just '$' -> do Just '$' <- char
+                                          Just '$' <- char
+                                          continue (return $ Lit $ T.pack "$")
+                           _        -> continue pVar
+        _        -> continue pLit
+  where
+    continue x = liftM2 (:) x pFrags
+
+pLit :: Parser Frag
+pLit = do
+    s <- takeWhile (/= '$')
+    return $ Lit s
+
+pVar :: Parser Frag
+pVar = do
+    Just '$' <- char
+    c <- peek
+    case c of
+        Just '{' -> do Just '{' <- char
+                       v <- pIdentifier
+                       c' <- peek
+                       case c' of
+                         Just '}' -> do Just '}' <- char
+                                        return $ Var v True
+                         _        -> liftM parseError pos
+        _        -> do v <- pIdentifier
+                       return $ Var v False
+
+pIdentifier :: Parser T.Text
+pIdentifier = do
+    c <- peek
+    case c of
+      Just c'
+          | isAlphaNum c' -> takeWhile isIdentifier
+          | otherwise     -> liftM parseError pos
+      Nothing             -> liftM parseError pos
+  where
+    isIdentifier c = or [isAlphaNum c, c `elem` "_'"]
+
+parseError :: (Int, Int) -> a
+parseError (row, col) = error $ "Invalid placeholder in string: line " ++
+                        show row ++ ", col " ++ show col
+
+-- -----------------------------------------------------------------------------
+-- Text parser
+
+type Parser = State (T.Text, Int, Int)
+
+char :: Parser (Maybe Char)
+char = do
+    (s, row, col) <- get
+    if T.null s
+      then return Nothing
+      else do c <- return $! T.head s
+              case c of
+                '\n' -> put (T.tail s, row + 1 :: Int, 1 :: Int)
+                _    -> put (T.tail s, row, col + 1 :: Int)
+              return $ Just c
+
+peek :: Parser (Maybe Char)
+peek = do
+    s <- get
+    c <- char
+    put s
+    return c
+
+peekSnd :: Parser (Maybe Char)
+peekSnd = do
+    s <- get
+    _ <- char
+    c <- char
+    put s
+    return c
+
+takeWhile :: (Char -> Bool) -> Parser T.Text
+takeWhile p = do
+    (s, row, col) <- get
+    case T.spanBy p s of
+      (x, s') -> do
+                  let xlines = T.lines x
+                      row' = row + fromIntegral (length xlines - 1)
+                      col' = case xlines of
+                               [] -> col -- Empty selection
+                               [sameLine] -> T.length sameLine
+                                             -- Taken from this line
+                               _  -> T.length (last xlines)
+                                     -- Selection extends
+                                     -- to next line at least
+                  put (s', row', col')
+                  return x
+
+pos :: Parser (Int, Int)
+pos = do
+    (_, row, col) <- get
+    return (row, col)
+
+runParser :: Parser a -> T.Text -> a
+runParser p s = evalState p (s, 1 :: Int, 1 :: Int)
+
+-- -----------------------------------------------------------------------------
+-- Example
+
+-- $example
+--
+-- Here is an example of a simple substitution:
+--
+-- > module Main where
+-- >
+-- > import qualified Data.ByteString.Lazy as S
+-- > import qualified Data.Text as T
+-- > import qualified Data.Text.Lazy.Encoding as E
+-- >
+-- > import Data.Text.Template
+-- >
+-- > -- | Create 'Context' from association list.
+-- > context :: [(T.Text, T.Text)] -> Context
+-- > context assocs x = maybe err id . lookup x $ assocs
+-- >   where err = error $ "Could not find key: " ++ T.unpack x
+-- >
+-- > main :: IO ()
+-- > main = S.putStr $ E.encodeUtf8 $ substitute helloTemplate helloContext
+-- >   where
+-- >     helloTemplate = T.pack "Hello, $name!\n"
+-- >     helloContext  = context [(T.pack "name", T.pack "Joe")]
+--
+-- The example can be simplified slightly by using the
+-- @OverloadedStrings@ language extension:
+--
+-- > {-# LANGUAGE OverloadedStrings #-}
+-- >
+-- > module Main where
+-- >
+-- > import qualified Data.ByteString.Lazy as S
+-- > import qualified Data.Text as T
+-- > import qualified Data.Text.Lazy.Encoding as E
+-- >
+-- > import Data.Text.Template
+-- >
+-- > -- | Create 'Context' from association list.
+-- > context :: [(T.Text, T.Text)] -> Context
+-- > context assocs x = maybe err id . lookup x $ assocs
+-- >   where err = error $ "Could not find key: " ++ T.unpack x
+-- >
+-- > main :: IO ()
+-- > main = S.putStr $ E.encodeUtf8 $ substitute helloTemplate helloContext
+-- >   where
+-- >     helloTemplate = "Hello, $name!\n"
+-- >     helloContext  = context [("name", "Joe")]
diff --git a/Text/Template.hs b/Text/Template.hs
deleted file mode 100644
--- a/Text/Template.hs
+++ /dev/null
@@ -1,263 +0,0 @@
--- | A simple string substitution library that supports \"$\"-based
--- substitution. Substitution uses the following rules:
---
---    * \"$$\" is an escape; it is replaced with a single \"$\".
---
---    * \"$identifier\" names a substitution placeholder matching a
---      mapping key of \"identifier\". \"identifier\" must spell a
---      Haskell identifier. The first non-identifier character after the
---      \"$\" character terminates this placeholder specification.
---
---    * \"${identifier}\" is equivalent to \"$identifier\". It is
---      required when valid identifier characters follow the placeholder
---      but are not part of the placeholder, such as
---      \"${noun}ification\".
---
--- Any other apperance of \"$\" in the string will result in an
--- 'Prelude.error' being raised.
---
--- Here is an example of a simple substitution:
---
--- > import qualified Data.ByteString.Lazy.Char8 as B
--- > import Text.Template
--- >
--- > context = Map.fromList . map packPair
--- >     where packPair (x, y) = (B.pack x, B.pack y)
--- >
--- > helloTemplate = B.pack "Hello, $name! Want some ${fruit}s?"
--- > helloContext = context [("name", "Johan"), ("fruit", "banana")]
--- >
--- > main = B.putStrLn $ substitute helloTemplate helloContext
---
--- If you render the same template multiple times it's faster to first
--- convert it to a more efficient representation using 'template' and
--- then rendering it using 'render'. In fact, all that 'substitute' does
--- is to combine these two steps.
-
-module Text.Template
-    (
-     -- * The @Template@ type.
-     Template,           -- abstract
-
-     -- * The @Context@ type.
-     Context,
-            
-     -- * Basic interface
-     template,           -- :: ByteString -> Template
-     render,             -- :: Template -> Context -> ByteString
-     substitute,         -- :: ByteString -> Context -> ByteString
-     showTemplate,       -- :: Template -> ByteString
-
-     -- * I\/O with 'Template's
-
-     -- ** Files
-     readTemplate,       -- :: FilePath -> IO Template
-     renderToFile,       -- :: FilePath -> Template -> Context -> IO ()
-
-     -- ** I\/O with Handles
-     hRender             -- :: Handle -> Template -> Context -> IO ()
-    ) where
-
-import Data.ByteString.Lazy.Char8 (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as B
-import Data.Int
-import Control.Monad.State
-import qualified Control.Monad.State as State
-import Data.Char
-import Data.Map (Map)
-import qualified Data.Map as Map
-import Prelude hiding (takeWhile)
-import System.IO
-
--- -----------------------------------------------------------------------------
-
--- | A repesentation of a 'Data.ByteString.Lazy.Char8.ByteString'
--- template, supporting efficient rendering.
-newtype Template = Template [Frag]
-
-instance Eq Template where
-    t1 == t2 = showTemplate t1 == showTemplate t2
-
-instance Show Template where
-    show = B.unpack . showTemplate
-
--- | Shows the template string.
-showTemplate :: Template -> ByteString
-showTemplate (Template fs) = B.concat $ map showFrag fs
-
-data Frag = Lit !ByteString | Var !ByteString !Bool
-
-instance Show Frag where
-    show = B.unpack . showFrag
-
-showFrag :: Frag -> ByteString
-showFrag (Var s b) | b         = B.concat [B.pack "${", s, B.pack "}"]
-                   | otherwise = B.concat [B.pack "$", s]
-showFrag (Lit s) = B.concatMap escape s
-    where escape c = case c of
-                       '$' -> B.pack "$$"
-                       c'  -> B.singleton c'
-
--- | A mapping with keys that match the placeholders in the template.
-type Context = Map ByteString ByteString
-
--- -----------------------------------------------------------------------------
--- Basic interface
-
--- | Creates a template from a template string.
-template :: ByteString -> Template
-template = runParser pTemplate
-
-pTemplate :: Parser Template
-pTemplate = pFrags >>= return . Template
-
-pFrags :: Parser [Frag]
-pFrags = do
-  c <- peek
-  case c of
-    Nothing  -> return []
-    Just '$' -> do c' <- peekSnd
-                   case c' of
-                     Just '$' -> do Just '$' <- char
-                                    Just '$' <- char
-                                    continue (return $ Lit $ B.pack "$")
-                     _        -> continue pVar
-    _        -> continue pLit
-    where
-      continue x = liftM2 (:) x pFrags
-
-pLit :: Parser Frag
-pLit = do
-  s <- takeWhile (/= '$')
-  return $ Lit s
-
-pVar :: Parser Frag
-pVar = do
-  Just '$' <- char
-  c <- peek
-  case c of
-    Just '{' -> do Just '{' <- char
-                   v <- pIdentifier
-                   c' <- peek
-                   case c' of
-                     Just '}' -> do Just '}' <- char
-                                    return $ Var v True
-                     _        -> liftM parseError pos
-    _        -> do v <- pIdentifier
-                   return $ Var v False
-
-pIdentifier :: Parser ByteString
-pIdentifier = do
-  c <- peek
-  case c of
-    Just c' -> if isAlphaNum c'
-                 then takeWhile isIdentifier
-                 else liftM parseError pos
-    Nothing -> liftM parseError pos
-    where
-      isIdentifier c = or [isAlphaNum c, c `elem` "_'"]
-
-parseError :: (Int64, Int64) -> a
-parseError (row, col) = error $ "Invalid placeholder in string: line " ++
-                        show row ++ ", col " ++ show col
-
--- | Performs the template substitution, returning a new
--- 'Data.ByteString.Lazy.Char8.ByteString'.
---
--- If a key is not found in the context an 'Prelude.error' is raised.
-render :: Template -> Context -> ByteString
-render (Template frags) ctx = B.concat $ map (renderFrag ctx) frags
-
-renderFrag :: Context -> Frag -> ByteString
-renderFrag _ (Lit s)     = s
-renderFrag ctx (Var x _) =
-    case Map.lookup x ctx of
-      Just s  -> s
-      Nothing -> error $ "Key not found: " ++ (show $ B.unpack x)
-
--- | Performs the template substitution, returning a new
--- 'Data.ByteString.Lazy.Char8.ByteString'. Note that
---
--- > substitute tmpl ctx == render (template tmpl) ctx
---
--- If a key is not found in the context an 'Prelude.error' is raised.
-substitute :: ByteString -> Context -> ByteString
-substitute tmpl = render (template tmpl)
-
--- -----------------------------------------------------------------------------
--- Files
-
--- | Reads a template from a file lazily.  Use 'text mode' on Windows to
--- interpret newlines
-readTemplate :: FilePath -> IO Template
-readTemplate f = (return . template) =<< B.readFile f
-
--- | Renders and writes a template to a file. This is more efficient
--- than first 'render'ing the template to a
--- 'Data.ByteString.Lazy.Char8.ByteString' and then writing it to a file
--- using 'Data.ByteString.Lazy.Char8.writeFile'.
-renderToFile :: FilePath -> Template -> Context -> IO ()
-renderToFile f tmpl = B.writeFile f . render tmpl
-
--- -----------------------------------------------------------------------------
--- I/O with Handles
-
--- | Renders and writes a template to a 'System.IO.Handle'. This is more
--- efficient than first 'render'ing the template to a
--- 'Data.ByteString.Lazy.Char8.ByteString' and then writing it to a
--- 'System.IO.Handle' using 'Data.ByteString.Lazy.Char8.hPutStr'.
-hRender :: Handle -> Template -> Context -> IO ()
-hRender h (Template frags) ctx = mapM_ (B.hPut h . renderFrag ctx) frags
-
--- -----------------------------------------------------------------------------
--- ByteString parser
-
-type Parser = State (ByteString, Int64, Int64)
-    
-char :: Parser (Maybe Char)
-char = do
-  (s, row, col) <- get
-  if B.null s
-    then return Nothing
-    else do c <- return $! B.head s
-            case c of
-              '\n' -> put (B.tail s, row + 1 :: Int64, 1 :: Int64)
-              _    -> put (B.tail s, row, col + 1 :: Int64)
-            return $ Just c
-
-peek :: Parser (Maybe Char)
-peek = do
-  s <- get
-  c <- char
-  put s
-  return c
-
-peekSnd :: Parser (Maybe Char)
-peekSnd = do
-  s <- get
-  char
-  c <- char
-  put s
-  return c
-
-takeWhile :: (Char -> Bool) -> Parser ByteString
-takeWhile p = do
-  (s, row, col) <- get
-  case B.span p s of
-    (x, s') -> do 
-                let newlines = B.elemIndices '\n' x
-                    n = B.length x
-                    row' = row + fromIntegral (length newlines)
-                    col' = case newlines of
-                             [] -> col + n
-                             _  -> n - last newlines
-                put (s', row', col')
-                return x
-
-pos :: Parser (Int64, Int64)
-pos = do
-  (_, row, col) <- get
-  return (row, col)
-
-runParser :: Parser a -> ByteString -> a
-runParser p s = evalState p (s, 1 :: Int64, 1 :: Int64)
diff --git a/examples/Hello.hs b/examples/Hello.hs
new file mode 100644
--- /dev/null
+++ b/examples/Hello.hs
@@ -0,0 +1,18 @@
+module Main where
+
+import qualified Data.ByteString.Lazy as S
+import qualified Data.Text as T
+import qualified Data.Text.Lazy.Encoding as E
+
+import Data.Text.Template
+
+-- | Create 'Context' from association list.
+context :: [(T.Text, T.Text)] -> Context
+context assocs x = maybe err id . lookup x $ assocs
+  where err = error $ "Could not find key: " ++ T.unpack x
+
+main :: IO ()
+main = S.putStr $ E.encodeUtf8 $ substitute helloTemplate helloContext
+  where
+    helloTemplate = T.pack "Hello, $name!\n"
+    helloContext  = context [(T.pack "name", T.pack "Joe")]
diff --git a/examples/OverloadedHello.hs b/examples/OverloadedHello.hs
new file mode 100644
--- /dev/null
+++ b/examples/OverloadedHello.hs
@@ -0,0 +1,20 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import qualified Data.ByteString.Lazy as S
+import qualified Data.Text as T
+import qualified Data.Text.Lazy.Encoding as E
+
+import Data.Text.Template
+
+-- | Create 'Context' from association list.
+context :: [(T.Text, T.Text)] -> Context
+context assocs x = maybe err id . lookup x $ assocs
+  where err = error $ "Could not find key: " ++ T.unpack x
+
+main :: IO ()
+main = S.putStr $ E.encodeUtf8 $ substitute helloTemplate helloContext
+  where
+    helloTemplate = "Hello, $name!\n"
+    helloContext  = context [("name", "Joe")]
diff --git a/template.cabal b/template.cabal
--- a/template.cabal
+++ b/template.cabal
@@ -1,29 +1,32 @@
-name:               template
-version:            0.1.1.1
-description:        Simple string substitution library that supports
-                    \"$\"-based substitution.  Meant to be used when
-                    Text.Printf or string concatenation would lead to
-                    code that is hard to read but when a full blown
-                    templating system might be overkill.
-synopsis:           Simple string substitution
-category:           Text
-license:            BSD3
-license-file:       LICENSE
-author:             Johan Tibell
-maintainer:         johan.tibell@gmail.com
+name:                template
+version:             0.2
+description:
+  Simple string substitution library that supports \"$\"-based
+  substitution.  Meant to be used when Text.Printf or string
+  concatenation would lead to code that is hard to read but when a
+  full blown templating system is overkill.
+synopsis:            Simple string substitution
+category:            Text
+license:             BSD3
+license-file:        LICENSE
+author:              Johan Tibell <johan.tibell@gmail.com>
+maintainer:          Johan Tibell <johan.tibell@gmail.com>
 build-type:          Simple
-cabal-version:       >= 1.2
-
-flag split-base
-  description: Chooce the new smaller, split-up base package.
+cabal-version:       >= 1.6
+extra-source-files:  examples/*.hs
 
 library
-  exposed-modules:  Text.Template
+  exposed-modules:  Data.Text.Template
 
-  if flag(split-base)
-    build-depends:  base >= 3, bytestring, containers
-  else
-    build-depends:  base < 3
-  build-depends:    mtl
+  build-depends:
+    base       < 5,
+    mtl,
+    text == 0.7.*
 
-  ghc-options:      -funbox-strict-fields -Wall
+  ghc-options: -funbox-strict-fields -Wall
+  if impl(ghc >= 6.8)
+    ghc-options: -fwarn-tabs
+
+source-repository head
+  type:     git
+  location: git://github.com/tibbe/template.git
