diff --git a/Yesod/Comments.hs b/Yesod/Comments.hs
--- a/Yesod/Comments.hs
+++ b/Yesod/Comments.hs
@@ -24,10 +24,10 @@
 import Yesod.Comments.Form
 import Yesod.Comments.View
 
-addComments :: (RenderMessage m FormMessage, YesodComments m) => ThreadId -> GWidget s m ()
+addComments :: (RenderMessage m FormMessage, YesodComments m) => ThreadId -> WidgetT m IO ()
 addComments thread = do
-    comments  <- lift $ csLoad commentStorage (Just thread)
-    mudetails <- lift $ currentUserDetails
+    comments  <- handlerToWidget $ csLoad commentStorage (Just thread)
+    mudetails <- handlerToWidget $ currentUserDetails
 
     [whamlet|
         <div .yesod_comments>
diff --git a/Yesod/Comments/Core.hs b/Yesod/Comments/Core.hs
--- a/Yesod/Comments/Core.hs
+++ b/Yesod/Comments/Core.hs
@@ -1,6 +1,8 @@
 {-# LANGUAGE QuasiQuotes       #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE ConstraintKinds   #-}
+{-# LANGUAGE TypeFamilies      #-}
 -------------------------------------------------------------------------------
 -- |
 -- Module      :  Yesod.Comments.Core
@@ -60,23 +62,23 @@
 --   necessary actions are accomplished through these 5 functions.
 --   Currently, only @persistStorage@ is available.
 data CommentStorage s m = CommentStorage
-    { csGet    :: ThreadId -> CommentId -> GHandler s m (Maybe Comment)
-    , csStore  :: Comment -> GHandler s m ()
-    , csUpdate :: Comment -> Comment -> GHandler s m ()
-    , csDelete :: Comment -> GHandler s m ()
+    { csGet    :: ThreadId -> CommentId -> HandlerT m IO (Maybe Comment)
+    , csStore  :: Comment -> HandlerT m IO ()
+    , csUpdate :: Comment -> Comment -> HandlerT m IO ()
+    , csDelete :: Comment -> HandlerT m IO ()
 
     -- | Pass @Nothing@ to get all comments site-wide.
-    , csLoad   :: Maybe ThreadId -> GHandler s m [Comment]
+    , csLoad   :: Maybe ThreadId -> HandlerT m IO [Comment]
     }
 
-class YesodAuth m => YesodComments m where
+class YesodAuthPersist m => YesodComments m where
     -- | How to store and load comments from persistent storage.
     commentStorage :: CommentStorage s m
 
     -- | If @Nothing@ is returned, the user cannot add a comment. This can
     --   be used to blacklist users. Note that comments left by them will
     --   still appear until manually deleted.
-    userDetails :: AuthId m -> GHandler s m (Maybe UserDetails)
+    userDetails :: AuthId m -> HandlerT m IO (Maybe UserDetails)
 
     -- | A thread's route. Currently, only used for linking back from the
     --   admin subsite.
diff --git a/Yesod/Comments/Form.hs b/Yesod/Comments/Form.hs
--- a/Yesod/Comments/Form.hs
+++ b/Yesod/Comments/Form.hs
@@ -31,7 +31,7 @@
 
 import qualified Data.Text as T
 
-type Form s m x = Html -> MForm s m (FormResult x, GWidget s m ())
+type Form m x = Html -> MForm (HandlerT m IO) (FormResult x, WidgetT m IO ())
 
 data CommentForm = CommentForm
     { formUser    :: UserDetails
@@ -39,7 +39,7 @@
     , formComment :: Markdown
     }
 
-commentFromForm :: YesodComments m => CommentForm -> GHandler s m Comment
+commentFromForm :: YesodComments m => CommentForm -> HandlerT m IO Comment
 commentFromForm cf = do
     now <- liftIO getCurrentTime
     ip  <- fmap (show . remoteHost) waiRequest
@@ -57,14 +57,14 @@
         }
 
     where
-        getNextCommentId :: YesodComments m => ThreadId -> GHandler s m CommentId
+        getNextCommentId :: YesodComments m => ThreadId -> HandlerT m IO CommentId
         getNextCommentId tid = go =<< csLoad commentStorage (Just tid)
 
-        go :: YesodComments m => [Comment] -> GHandler s m CommentId
+        go :: YesodComments m => [Comment] -> HandlerT m IO CommentId
         go [] = return 1
         go cs = return $ maximum (map commentId cs) + 1
 
-commentForm :: RenderMessage m FormMessage => ThreadId -> UserDetails -> Maybe Comment -> Form s m CommentForm
+commentForm :: RenderMessage m FormMessage => ThreadId -> UserDetails -> Maybe Comment -> Form m CommentForm
 commentForm thread udetails mcomment = renderBootstrap $ CommentForm
     <$> pure udetails <*> pure thread
     <*> areq markdownField commentLabel (fmap cContent mcomment)
@@ -74,30 +74,28 @@
         commentLabel = "Comment" { fsTooltip = Just "Comments are parsed as pandoc-style markdown." }
 
 -- | Run the form and stores the comment on successful submission
-runForm :: YesodComments m => ThreadId -> Maybe UserDetails -> GWidget s m ()
+runForm :: YesodComments m => ThreadId -> Maybe UserDetails -> WidgetT m IO ()
 runForm = runFormWith Nothing $ \cf -> do
-    tm <- getRouteToMaster
-
     csStore commentStorage =<< commentFromForm cf
     setMessage "comment added."
 
     -- redirect to current route
-    maybe notFound (redirect . tm) =<< getCurrentRoute
+    maybe notFound redirect =<< getCurrentRoute
 
 -- | Both handle form submission and present form HTML. On @FormSuccess@,
 --   run the given function on the submitted value.
 runFormWith :: YesodComments m
             => Maybe Comment
-            -> (CommentForm -> GHandler s m ())
+            -> (CommentForm -> HandlerT m IO ())
             -> ThreadId
             -> Maybe UserDetails
-            -> GWidget s m ()
+            -> WidgetT m IO ()
 runFormWith _ _ _ Nothing = [whamlet|<h4>Please ^{login} to post a comment.|]
 runFormWith mcomment f thread (Just ud@(UserDetails _ name email)) = do
-    ((res, form), enctype) <- lift $ runFormPost (commentForm thread ud mcomment)
+    ((res, form), enctype) <- handlerToWidget $ runFormPost (commentForm thread ud mcomment)
 
     case res of
-        FormSuccess cf -> lift $ f cf
+        FormSuccess cf -> handlerToWidget $ f cf
         _              -> return ()
 
     [whamlet|
@@ -118,9 +116,9 @@
                     <button .btn .primary type="submit">Add comment
     |]
 
-login :: Yesod m => GWidget s m ()
+login :: Yesod m => WidgetT m IO ()
 login = do
-    mroute <- lift $ do
+    mroute <- handlerToWidget $ do
         setUltDestCurrent
         fmap authRoute getYesod
 
diff --git a/Yesod/Comments/Management.hs b/Yesod/Comments/Management.hs
--- a/Yesod/Comments/Management.hs
+++ b/Yesod/Comments/Management.hs
@@ -3,6 +3,7 @@
 {-# LANGUAGE TemplateHaskell             #-}
 {-# LANGUAGE FlexibleInstances           #-}
 {-# LANGUAGE FlexibleContexts            #-}
+{-# LANGUAGE RankNTypes                  #-}
 {-# LANGUAGE TypeFamilies                #-}
 {-# LANGUAGE MultiParamTypeClasses       #-}
 {-# LANGUAGE OverloadedStrings           #-}
@@ -20,9 +21,8 @@
 --
 -------------------------------------------------------------------------------
 module Yesod.Comments.Management
-    ( CommentsAdmin
-    , getCommentsAdmin
-    , Route(..)
+    ( getCommentsAdmin
+    , module Yesod.Comments.Management.Routes
     ) where
 
 import Yesod
@@ -32,28 +32,24 @@
 import Yesod.Comments.Utils
 import Yesod.Comments.Form
 import Yesod.Comments.View
+import Yesod.Comments.Management.Routes
 
 import Control.Monad (forM, unless)
 import Data.List (sortBy, nub)
 import Data.Text (Text)
 import Data.Time (UTCTime)
-import Language.Haskell.TH.Syntax hiding (lift)
 
-data CommentsAdmin = CommentsAdmin
-
 getCommentsAdmin :: a -> CommentsAdmin
 getCommentsAdmin = const CommentsAdmin
 
-mkYesodSub "CommentsAdmin"
-    [ ClassP ''YesodComments [ VarT $ mkName "master" ] ]
-    [parseRoutes|
-        /                            CommentsR      GET
-        /edit/#ThreadId/#CommentId   EditCommentR   GET POST
-        /delete/#ThreadId/#CommentId DeleteCommentR GET POST
-        |]
+instance YesodComments master => YesodSubDispatch CommentsAdmin (HandlerT master IO)
+    where yesodSubDispatch = $(mkYesodSubDispatch resourcesCommentsAdmin)
 
-getCommentsR :: YesodComments m => GHandler CommentsAdmin m RepHtml
-getCommentsR = do
+type Handler a = forall master. YesodComments master
+               => HandlerT CommentsAdmin (HandlerT master IO) a
+
+getCommentsR :: Handler RepHtml
+getCommentsR = lift $ do
     comments <- getThreadedComments
 
     layout "Your comments" [whamlet|
@@ -66,35 +62,72 @@
                     ^{showComments cs}
         |]
 
-getEditCommentR :: YesodComments m => ThreadId -> CommentId -> GHandler CommentsAdmin m RepHtml
-getEditCommentR thread cid = withUserComment thread cid $ \c -> do
-    ud <- requireUserDetails
+getEditCommentR :: ThreadId -> CommentId -> Handler RepHtml
+getEditCommentR thread cid = do
+    ud@(UserDetails _ name email) <- lift $ requireUserDetails
 
-    layout "Edit comment" [whamlet|
-        ^{runFormEdit c thread (Just ud)}
-        |]
+    -- TODO: Duplication with withUserComment
+    comment <- lift $ do
+        mcomment <- csGet commentStorage thread cid
+        case mcomment of
+            Just comment -> do
+                _    <- requireAuthId
+                mine <- isCommentingUser comment
+                unless mine $ permissionDenied "you can only manage your own comments"
+                return comment
 
-postEditCommentR :: YesodComments m => ThreadId -> CommentId -> GHandler CommentsAdmin m RepHtml
+            Nothing -> notFound
+
+    ((res, form), enctype) <- lift $ runFormPost (commentForm thread ud (Just comment))
+
+    case res of
+        FormSuccess cf -> do
+            lift $ csUpdate commentStorage comment $ comment { cContent = formComment cf }
+            setMessage "comment updated."
+            redirect CommentsR
+        _ -> return ()
+
+    -- TODO: Duplication with runFormWith
+    lift $ layout "Edit comment" [whamlet|
+        <div .avatar>
+            <a target="_blank" title="change your profile picture at gravatar" href="http://gravatar.com/emails/">
+                <img src="#{gravatar 48 email}">
+
+        <div .input>
+            <form enctype="#{enctype}" method="post" .form-stacked>
+                <div .clearfix .optional>
+                    <label for="username">Username
+                    <div .input>
+                        <p #username>#{name}
+
+                ^{form}
+
+                <div .actions>
+                    <button .btn .primary type="submit">Add comment
+    |]
+
+postEditCommentR :: ThreadId -> CommentId -> Handler RepHtml
 postEditCommentR = getEditCommentR
 
-getDeleteCommentR :: YesodComments m => ThreadId -> CommentId -> GHandler CommentsAdmin m RepHtml
-getDeleteCommentR _ _ = layout "Delete comment" [whamlet|
+getDeleteCommentR :: ThreadId -> CommentId -> Handler RepHtml
+getDeleteCommentR _ _ = lift $ layout "Delete comment" [whamlet|
     <p>Are you sure?
     <form method="post" .form-stacked>
         <div .actions>
             <button .btn .btn-danger type="submit">Delete comment
     |]
 
-postDeleteCommentR :: YesodComments m => ThreadId -> CommentId -> GHandler CommentsAdmin m RepHtml
-postDeleteCommentR thread cid = withUserComment thread cid $ \c -> do
-    tm <- getRouteToMaster
-    csDelete commentStorage c
-    setMessage "comment deleted."
-    redirect $ tm CommentsR
+postDeleteCommentR :: ThreadId -> CommentId -> Handler RepHtml
+postDeleteCommentR thread cid = do
+    lift $ withUserComment thread cid $ \c -> do
+        csDelete commentStorage c
+        setMessage "comment deleted."
 
+    redirect CommentsR
+
 -- | Return tuples of thread id and associated comments sorted by most
 --   recently commented on thread.
-getThreadedComments :: YesodComments m => GHandler s m [(ThreadId, [Comment])]
+getThreadedComments :: YesodComments m => HandlerT m IO [(ThreadId, [Comment])]
 getThreadedComments = do
     allComments <- csLoad commentStorage Nothing
     allThreads  <- forM allComments $ \comment -> do
@@ -109,7 +142,7 @@
     where
         latest :: (ThreadId, [Comment]) -> (ThreadId, [Comment]) -> Ordering
         latest (t1, cs1) (t2, cs2) =
-            -- reversed comparason so more recent threads sort first
+            -- reversed comparison so more recent threads sort first
             case compare (latest' cs1) (latest' cs2) of
                 EQ -> compare t1 t2
                 GT -> LT
@@ -120,7 +153,7 @@
 
 -- | If the comment belongs to the currently logged in user, runs the
 --   action on it. Otherwise, halts with @permissionDenied@.
-withUserComment :: YesodComments m => ThreadId -> CommentId -> (Comment -> GHandler s m RepHtml) -> GHandler s m RepHtml
+withUserComment :: YesodComments m => ThreadId -> CommentId -> (Comment -> HandlerT m IO a) -> HandlerT m IO a
 withUserComment thread cid f = do
     mcomment <- csGet commentStorage thread cid
     case mcomment of
@@ -132,17 +165,7 @@
 
         Nothing -> notFound
 
--- | Runs the form and updates the comment on success
-runFormEdit :: YesodComments m => Comment -> ThreadId -> Maybe UserDetails -> GWidget CommentsAdmin m ()
-runFormEdit comment = runFormWith (Just comment) $ \cf -> do
-    tm <- getRouteToMaster
-
-    csUpdate commentStorage comment $ comment { cContent = formComment cf }
-    setMessage "comment updated."
-
-    redirect $ tm CommentsR
-
-layout :: Yesod m => Text -> GWidget s m () -> GHandler s m RepHtml
+layout :: Yesod m => Text -> WidgetT m IO () -> HandlerT m IO RepHtml
 layout title inner = defaultLayout $ do
     setTitle $ toHtml title
 
diff --git a/Yesod/Comments/Management/Routes.hs b/Yesod/Comments/Management/Routes.hs
new file mode 100644
--- /dev/null
+++ b/Yesod/Comments/Management/Routes.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE FlexibleContexts            #-}
+{-# LANGUAGE MultiParamTypeClasses       #-}
+{-# LANGUAGE QuasiQuotes                 #-}
+{-# LANGUAGE TemplateHaskell             #-}
+{-# LANGUAGE TypeFamilies                #-}
+-------------------------------------------------------------------------------
+-- |
+-- Module        : Yesod.Comments.Management.Routes
+-- Copyright     : Patrick Brisbin
+-- License       : as-is
+--
+-- Maintainer    : Patrick Brisbin <me@pbrisbin.com>
+-- Stability     : unstable
+-- Portability   : unportable
+--
+-------------------------------------------------------------------------------
+module Yesod.Comments.Management.Routes where
+
+import Yesod
+import Yesod.Comments.Core
+
+data CommentsAdmin = CommentsAdmin
+
+mkYesodSubData "CommentsAdmin" [parseRoutes|
+    /                            CommentsR      GET
+    /edit/#ThreadId/#CommentId   EditCommentR   GET POST
+    /delete/#ThreadId/#CommentId DeleteCommentR GET POST
+    |]
diff --git a/Yesod/Comments/Storage.hs b/Yesod/Comments/Storage.hs
--- a/Yesod/Comments/Storage.hs
+++ b/Yesod/Comments/Storage.hs
@@ -27,9 +27,9 @@
 
 import Data.Text (Text)
 import Data.Time (UTCTime)
-import Database.Persist.GenericSql (SqlPersist)
+import Database.Persist.Sql (SqlPersist)
 
-share [mkPersist sqlSettings, mkMigrate "migrateComments"] [persist|
+share [mkPersist sqlSettings, mkMigrate "migrateComments"] [persistUpperCase|
 SqlComment
     threadId  Text Eq noreference
     commentId Int    Eq Asc noreference
diff --git a/Yesod/Comments/Utils.hs b/Yesod/Comments/Utils.hs
--- a/Yesod/Comments/Utils.hs
+++ b/Yesod/Comments/Utils.hs
@@ -32,7 +32,7 @@
 import qualified Network.Gravatar as G
 
 -- | Map the commenter's id to user details or return defaults.
-commentUserDetails :: YesodComments m => Comment -> GHandler s m UserDetails
+commentUserDetails :: YesodComments m => Comment -> HandlerT m IO UserDetails
 commentUserDetails c =
     return . fromMaybe (defaultUserDetails c) =<<
         case (cIsAuth c, fromPathPiece (cUserName c)) of
@@ -40,7 +40,7 @@
             _                -> return Nothing
 
 -- | Returns @Nothing@ if user is not authenticated
-currentUserDetails :: YesodComments m => GHandler s m (Maybe UserDetails)
+currentUserDetails :: YesodComments m => HandlerT m IO (Maybe UserDetails)
 currentUserDetails = do
     muid <- maybeAuthId
     case muid of
@@ -48,7 +48,7 @@
         _        -> return Nothing
 
 -- | Halts with @permissionDenied@ if user is not authenticated
-requireUserDetails :: YesodComments m => GHandler s m (UserDetails)
+requireUserDetails :: YesodComments m => HandlerT m IO (UserDetails)
 requireUserDetails = do
     mudetails <- currentUserDetails
     case mudetails of
@@ -64,7 +64,7 @@
 gravatar :: Int -> Text -> String
 gravatar s = G.gravatar defaultConfig { gDefault = Just MM, gSize = Just $ Size s }
 
-isCommentingUser :: YesodComments m => Comment -> GHandler s m Bool
+isCommentingUser :: YesodComments m => Comment -> HandlerT m IO Bool
 isCommentingUser comment = do
     mudetails <- currentUserDetails
     cudetails <- commentUserDetails comment
diff --git a/Yesod/Comments/View.hs b/Yesod/Comments/View.hs
--- a/Yesod/Comments/View.hs
+++ b/Yesod/Comments/View.hs
@@ -25,7 +25,7 @@
 import Data.Time.Format.Human
 import Data.Monoid (mempty)
 
-showComments :: YesodComments m => [Comment] -> GWidget s m ()
+showComments :: YesodComments m => [Comment] -> WidgetT m IO ()
 showComments comments = [whamlet|
     <div .list>
         $if not $ null comments
@@ -42,11 +42,11 @@
         helper 1 = "1 comment"
         helper n = show n ++ " comments"
 
-showComment :: YesodComments m => Comment -> GWidget s m ()
+showComment :: YesodComments m => Comment -> WidgetT m IO ()
 showComment comment = do
-    mine                     <- lift $ isCommentingUser comment
-    commentTimestamp         <- lift . liftIO . humanReadableTime $ cTimeStamp comment
-    UserDetails _ name email <- lift $ commentUserDetails comment
+    mine                     <- handlerToWidget $ isCommentingUser comment
+    commentTimestamp         <- handlerToWidget . liftIO . humanReadableTime $ cTimeStamp comment
+    UserDetails _ name email <- handlerToWidget $ commentUserDetails comment
 
     let anchor = "comment_" ++ show (commentId comment)
 
@@ -72,7 +72,7 @@
 -- | Edit and Delete links if configured
 commentControls :: Maybe (ThreadId -> CommentId -> Route m) -- ^ Edit route
                 -> Maybe (ThreadId -> CommentId -> Route m) -- ^ Delete route
-                -> ThreadId -> CommentId -> GWidget s m ()
+                -> ThreadId -> CommentId -> WidgetT m IO ()
 commentControls e@(Just _) d@(Just _) thread cid = [whamlet|
     ^{commentControls e Nothing thread cid}
     \ | 
diff --git a/yesod-comments.cabal b/yesod-comments.cabal
--- a/yesod-comments.cabal
+++ b/yesod-comments.cabal
@@ -1,5 +1,5 @@
 name:                yesod-comments
-version:             0.8.3
+version:             0.9.0
 synopsis:            A generic comments interface for a Yesod application
 description:         A generic comments interface for a Yesod application
 homepage:            http://github.com/pbrisbin/yesod-comments
@@ -19,6 +19,7 @@
                  , Yesod.Comments.Utils
                  , Yesod.Comments.Form
                  , Yesod.Comments.View
+                 , Yesod.Comments.Management.Routes
                  , Yesod.Comments.Management
 
   -- Packages needed in order to build this package.
@@ -27,12 +28,12 @@
                , bytestring          >= 0.9.1  && < 0.11
                , friendly-time       >= 0.2   && < 0.3
                , gravatar            >= 0.5   && < 0.6
-               , persistent          >= 1.0   && < 1.2
+               , persistent          >= 1.2   && < 1.3
                , wai                 >= 1.3   && < 1.5
-               , yesod               >= 1.1   && < 1.2
-               , yesod-auth          >= 1.1   && < 1.2
-               , yesod-form          >= 1.1   && < 1.3
-               , yesod-markdown      >= 0.4   && < 0.8
+               , yesod               >= 1.2   && < 1.3
+               , yesod-auth          >= 1.2   && < 1.3
+               , yesod-form          >= 1.3   && < 1.4
+               , yesod-markdown      >= 0.8   && < 0.9
 
                , template-haskell
                , directory
