diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Wai Routes (wai-routes-0.4.1)
+Wai Routes (wai-routes-0.5.0)
 ==============================
 
 [![Build Status](https://travis-ci.org/ajnsit/wai-routes.png)](https://travis-ci.org/ajnsit/wai-routes)
@@ -82,4 +82,5 @@
 * 0.3.4 : Added 'liftResourceT' to lift a ResourceT into HandlerM
 * 0.4.0 : Wai 2 compatibility. Replaced 'liftResourceT' with 'lift'
 * 0.4.1 : showRoute now returns "/" instead of ""
+* 0.5.0 : Added raw,text,html,json helpers. Update to wai-2.1.
 
diff --git a/examples/Example.hs b/examples/Example.hs
--- a/examples/Example.hs
+++ b/examples/Example.hs
@@ -9,7 +9,7 @@
 import Network.HTTP.Types
 import qualified Data.Text as T
 import Data.Text (Text)
-import Data.Aeson
+import Data.Aeson hiding (json)
 import Data.IORef
 import qualified Data.Map as M
 import Control.Monad.Trans
@@ -44,13 +44,6 @@
 
 -- Handlers
 
--- Standard Json output
-jsonOut :: ToJSON a => a -> Response
-jsonOut = responseLBS status200 jsonHeaders . encode
-  where
-    jsonHeaders :: ResponseHeaders
-    jsonHeaders = [contentType typeJson]
-
 -- Util: Fetch the database
 getDB :: HandlerM MyRoute DB
 getDB = do
@@ -59,7 +52,8 @@
 
 -- Display the possible actions
 getHomeR :: Handler MyRoute
-getHomeR = runHandlerM $ return $ jsonOut $ M.fromList (
+getHomeR = runHandlerM $ do
+  json $ M.fromList (
                  [("description", [["Simple User database Example"]])
                  ,("links"
                   ,[["home",  showRoute HomeR]
@@ -74,10 +68,9 @@
 getUsersR = runHandlerM $ do
   db <- getDB
   let dblinks = map linkify db
-  let out = M.fromList (
+  json $ M.fromList (
           [("description", [["Users List"]])
           ,("links", dblinks)] :: [(Text, [[Text]])] )
-  return $ jsonOut out
   where
     linkify user = [userName user, showRoute $ UserR (userId user) UserRootR]
 
@@ -86,9 +79,9 @@
 getUserRootR i = runHandlerM $ do
   db <- getDB
   case ulookup i db of
-    Nothing -> return $ jsonOut ("ERROR: User not found" :: Text)
+    Nothing -> json ("ERROR: User not found" :: Text)
     Just user -> do
-      let out = M.fromList (
+      json $ M.fromList (
             [("description", [["User details"]])
             ,("data"
              ,[["Id",   T.pack $ show $ userId user]
@@ -102,24 +95,21 @@
               ]
              )
             ] :: [(Text, [[Text]])] )
-      return $ jsonOut out
   where
     ulookup _ [] = Nothing
     ulookup ui (u:us) = if userId u == ui then Just u else ulookup ui us
 
 -- Delete a user: GET
 getUserDeleteR :: Int -> Handler MyRoute
-getUserDeleteR _ = runHandlerM $ return $ jsonOut err
-  where err = ["DELETE","please use POST"]::[Text]
+getUserDeleteR _ = runHandlerM $ json (["DELETE","please use POST"]::[Text])
 
 -- Delete a user: POST
 postUserDeleteR :: Int -> Handler MyRoute
-postUserDeleteR _ = runHandlerM $ return $ jsonOut err
-  where err = ["DELETE","not implemented"]::[Text]
+postUserDeleteR _ = runHandlerM $ json (["DELETE","not implemented"]::[Text])
 
 -- Demonstrate skipping routes
 getSkipR :: [Text] -> Handler MyRoute
-getSkipR _ = runHandlerM next
+getSkipR _ = runHandlerM $ next >> return ()
 
 -- Initial database
 initdb :: [User]
@@ -137,8 +127,7 @@
 |]
 
 getSkippedR :: [Text] -> Handler MySkippedRoute
-getSkippedR rest = runHandlerM $ return $ jsonOut err
-  where err = ["SKIPPED ROUTE: "] ++ rest
+getSkippedR rest = runHandlerM $ json $ ["SKIPPED ROUTE: "] ++ rest
 
 -- The application that uses our route
 -- NOTE: We use the Route Monad to simplify routing
diff --git a/src/Network/Wai/Middleware/Routes.hs b/src/Network/Wai/Middleware/Routes.hs
--- a/src/Network/Wai/Middleware/Routes.hs
+++ b/src/Network/Wai/Middleware/Routes.hs
@@ -53,6 +53,12 @@
     , runHandlerM            -- | Run a HandlerM to get a Handler
     , request                -- | Access the request data
     , master                 -- | Access the master datatype
+    , header                 -- | Add a header to the response
+    , status                 -- | Set the response status
+    , raw                    -- | Set the raw response body
+    , json                   -- | Set the json response body
+    , text                   -- | Set the text response body
+    , html                   -- | Set the html response body
     , next                   -- | Run the next application in the stack
   )
   where
diff --git a/src/Network/Wai/Middleware/Routes/ContentTypes.hs b/src/Network/Wai/Middleware/Routes/ContentTypes.hs
--- a/src/Network/Wai/Middleware/Routes/ContentTypes.hs
+++ b/src/Network/Wai/Middleware/Routes/ContentTypes.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving, TypeFamilies #-}
+{-# LANGUAGE OverloadedStrings, TypeFamilies #-}
 
 {- |
 Module      :  Network.Wai.Middleware.Routes.ContentTypes
@@ -29,8 +29,8 @@
 
 -- | Creates a content type header
 -- Ready to be passed to `responseLBS`
-contentType :: ByteString -> (HeaderName, ByteString)
-contentType typ = ("Content-Type", typ)
+contentType :: HeaderName
+contentType = "Content-Type"
 
 typeHtml :: ByteString
 typeHtml = "text/html; charset=utf-8"
diff --git a/src/Network/Wai/Middleware/Routes/Handler.hs b/src/Network/Wai/Middleware/Routes/Handler.hs
--- a/src/Network/Wai/Middleware/Routes/Handler.hs
+++ b/src/Network/Wai/Middleware/Routes/Handler.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}
 {- |
 Module      :  Network.Wai.Middleware.Routes.Handler
 Copyright   :  (c) Anupam Jain 2013
@@ -15,41 +15,127 @@
     , runHandlerM            -- | Run a HandlerM to get a Handler
     , request                -- | Access the request data
     , master                 -- | Access the master datatype
+    , header                 -- | Add a header to the response
+    , status                 -- | Set the response status
+    , raw                    -- | Set the raw response body
+    , json                   -- | Set the json response body
+    , text                   -- | Set the text response body
+    , html                   -- | Set the html response body
     , next                   -- | Run the next application in the stack
     )
     where
 
-import Network.Wai (Request, Response)
-import Control.Monad.Reader (ReaderT, ask, runReaderT, MonadReader, MonadIO, lift, MonadTrans)
+import Network.Wai (Request, Response, responseBuilder)
+import Control.Monad (liftM)
+import Control.Monad.State (StateT, get, put, modify, runStateT, MonadState, MonadIO, lift, MonadTrans)
 
 import Network.Wai.Middleware.Routes.Routes (RequestData, Handler, waiReq, runNext)
+import Network.Wai.Middleware.Routes.ContentTypes (contentType, typeHtml, typeJson, typePlain)
 
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Lazy as BL
+import Network.HTTP.Types.Header (HeaderName())
+import Network.HTTP.Types.Status (Status(), status200)
+
+import Data.Aeson (ToJSON)
+import qualified Data.Aeson as A
+
+import Data.Text.Lazy (Text)
+import qualified Data.Text.Lazy as T
+import Data.Text.Lazy.Encoding (encodeUtf8)
+
+import Blaze.ByteString.Builder (fromLazyByteString)
+
 -- | The internal implementation of the HandlerM monad
-newtype HandlerMI master m a = H { extractH :: ReaderT (HandlerState master) m a }
-    deriving (Monad, MonadIO, Functor, MonadTrans, MonadReader (HandlerState master))
+-- TODO: Should change this to StateT over ReaderT (but performance may suffer)
+newtype HandlerMI master m a = H { extractH :: StateT (HandlerState master) m a }
+    deriving (Monad, MonadIO, Functor, MonadTrans, MonadState (HandlerState master))
 
 -- | The HandlerM Monad
 type HandlerM master a = HandlerMI master IO a
 
 -- | The state kept in a HandlerM Monad
 data HandlerState master = HandlerState
-                { getMaster :: master
+                { getMaster      :: master
                 , getRequestData :: RequestData
+                , respHeaders    :: [(HeaderName, ByteString)]
+                , respStatus     :: Status
+                , respBody       :: BL.ByteString
+                , respResp       :: Maybe Response
                 }
 
 -- | "Run" HandlerM, resulting in a Handler
-runHandlerM :: HandlerM master Response -> Handler master
-runHandlerM h m r = runReaderT (extractH h) (HandlerState m r)
+runHandlerM :: HandlerM master () -> Handler master
+runHandlerM h m req = do
+  (_, state) <- runStateT (extractH h) (HandlerState m req [] status200 "" Nothing)
+  case respResp state of
+    Nothing -> return $ toResp state
+    Just resp -> return resp
 
+toResp :: HandlerState master -> Response
+toResp hs = responseBuilder (respStatus hs) (respHeaders hs) (fromLazyByteString $ respBody hs)
+
 -- | Get the master
 master :: HandlerM master master
-master = ask >>= return . getMaster
+master = liftM getMaster get
 
 -- | Get the request
 request :: HandlerM master Request
-request = ask >>= return . waiReq . getRequestData
+request = liftM (waiReq . getRequestData) get
 
+-- | Add a header to the application response
+-- TODO: Differentiate between setting and adding headers
+header :: HeaderName -> ByteString -> HandlerM master ()
+header h s = modify $ addHeader h s
+  where
+    addHeader :: HeaderName -> ByteString -> HandlerState master -> HandlerState master
+    addHeader h b s@(HandlerState {respHeaders=hs}) = s {respHeaders=(h,b):hs}
+
+-- | Set the response status
+status :: Status -> HandlerM master ()
+status s = modify $ setStatus s
+  where
+    setStatus :: Status -> HandlerState master -> HandlerState master
+    setStatus s st = st{respStatus=s}
+
+-- | Set the response body
+-- TODO: Add functions to append to body, and also to flush body contents
+raw :: BL.ByteString -> HandlerM master ()
+raw s = modify $ setBody s
+  where
+    setBody :: BL.ByteString -> HandlerState master -> HandlerState master
+    setBody s st = st{respBody=s}
+
+-- Standard response bodies
+
+-- | Set the body of the response to the JSON encoding of the given value. Also sets \"Content-Type\"
+-- header to \"application/json\".
+json :: ToJSON a => a -> HandlerM master ()
+json a = do
+  header contentType typeJson
+  raw $ A.encode a
+
+-- | Set the body of the response to the given 'Text' value. Also sets \"Content-Type\"
+-- header to \"text/plain\".
+text :: Text -> HandlerM master ()
+text t = do
+    header contentType typePlain
+    raw $ encodeUtf8 t
+
+-- | Set the body of the response to the given 'Text' value. Also sets \"Content-Type\"
+-- header to \"text/html\".
+html :: BL.ByteString -> HandlerM master ()
+html s = do
+    header contentType typeHtml
+    raw s
+
 -- | Run the next application
-next :: HandlerM master Response
-next = ask >>= H . lift . runNext . getRequestData
+next :: HandlerM master ()
+next = do
+  s <- get
+  resp <- lift $ runNext $ getRequestData s
+  modify $ setResp resp
+  where
+    setResp :: Response -> HandlerState master -> HandlerState master
+    setResp r st = st{respResp=Just r}
 
diff --git a/src/Network/Wai/Middleware/Routes/Routes.hs b/src/Network/Wai/Middleware/Routes/Routes.hs
--- a/src/Network/Wai/Middleware/Routes/Routes.hs
+++ b/src/Network/Wai/Middleware/Routes/Routes.hs
@@ -92,7 +92,7 @@
 app404 _master = runNext
 
 app405 :: Handler master
-app405 _master _req = return $ responseBuilder status405 [contentType typePlain] $ fromByteString "405 - Method Not Allowed"
+app405 _master _req = return $ responseBuilder status405 [(contentType, typePlain)] $ fromByteString "405 - Method Not Allowed"
 
 -- | Generates all the things needed for efficient routing,
 -- including your application's `Route` datatype, and
diff --git a/wai-routes.cabal b/wai-routes.cabal
--- a/wai-routes.cabal
+++ b/wai-routes.cabal
@@ -1,5 +1,5 @@
 name          : wai-routes
-version       : 0.4.1
+version       : 0.5.0
 cabal-version : >=1.10
 build-type    : Simple
 license       : MIT
@@ -63,19 +63,20 @@
 
 source-repository this
     type     : git
-    location : http://github.com/ajnsit/wai-routes/tree/v0.4.1
-    tag      : v0.4.1
+    location : http://github.com/ajnsit/wai-routes/tree/v0.5.0
+    tag      : v0.5.0
 
 library
-    build-depends  : base             >= 4.6.0.1  && < 4.7
-                   , wai              >= 2.0.0    && < 2.1
-                   , text             >= 0.11.3.1 && < 0.12
-                   , bytestring       >= 0.10.0.2 && < 0.11
-                   , http-types       >= 0.8.3    && < 0.9
-                   , blaze-builder    >= 0.3.3.2  && < 0.4
-                   , template-haskell >= 2.8.0.0  && < 2.9
-                   , yesod-routes     >= 1.2.0.6  && < 1.3
-                   , mtl              >= 2.1.2    && < 2.2
+    build-depends: base             >= 4.6.0.1  && < 4.7
+                 , wai              >= 2.0.0    && < 2.2
+                 , text             >= 0.11.3.1 && < 0.12
+                 , bytestring       >= 0.10.0.2 && < 0.11
+                 , http-types       >= 0.8.3    && < 0.9
+                 , blaze-builder    >= 0.3.3.2  && < 0.4
+                 , template-haskell >= 2.8.0.0  && < 2.9
+                 , yesod-routes     >= 1.2.0.6  && < 1.3
+                 , mtl              >= 2.1.2    && < 2.2
+                 , aeson            >= 0.7.0.1  && < 0.8
     exposed-modules: Network.Wai.Middleware.Routes
                      Network.Wai.Middleware.Routes.Routes
                      Network.Wai.Middleware.Routes.Monad
