diff --git a/Yesod/Paginator.hs b/Yesod/Paginator.hs
--- a/Yesod/Paginator.hs
+++ b/Yesod/Paginator.hs
@@ -11,13 +11,41 @@
 -- But uses an entirely different approach.
 --
 -- There are two pagination functions. One for arbitrary items where you
--- provide the list of things to be paginated, and one for paginating
--- directly out of the database, you provide the same filters as you
--- would to @selectList@.
+-- provide the list of things to be paginated:
 --
+-- > getSomeRoute = do
+-- >     things' <- getAllThings
+-- >
+-- >     (things, widget) <- paginate 10 things'
+-- >
+-- >     defaultLayout $ do
+-- >         [whamlet|
+-- >             $forall thing <- things
+-- >                 ^{showThing thing}
+-- >
+-- >             <div .pagination>
+-- >                  ^{widget}
+-- >             |]
+--
+-- And another for paginating directly out of the database, you provide
+-- the same filters as you would to @selectList@.
+--
+-- > getSomeRoute something = do
+-- >     -- note: things is [Entity val] just like selectList returns
+-- >     (things, widget) <- runDB $ selectPaginated 10 [SomeThing ==. something] []
+-- >
+-- >     defaultLayout $ do
+-- >         [whamlet|
+-- >             $forall thing <- things
+-- >                 ^{showThing $ entityVal thing}
+-- >
+-- >             <div .pagination>
+-- >                  ^{widget}
+-- >             |]
+--
 -- Both functions return a tuple: the first element being the list of
--- items to display on this page and the second being a widget showing
--- the pagination navagation links.
+-- items (or Entities) to display on this page and the second being a
+-- widget showing the pagination navagation links.
 --
 -------------------------------------------------------------------------------
 module Yesod.Paginator
@@ -26,32 +54,11 @@
     , paginationWidget
     ) where
 
-import Yesod -- TODO: minimal deps
-import Control.Monad (when)
+import Yesod
+import Yesod.Paginator.Widget
 import Control.Monad.Trans.Class (MonadTrans)
-import Data.Text     (Text)
-import Data.Maybe    (fromMaybe)
-import qualified Data.Text as T
 
--- | Paginate an existing list of items.
---
---   > getSomeRoute = do
---   >     things' <- getAllThings
---   >
---   >     (things, widget) <- paginate 10 things'
---   >
---   >     defaultLayout $ do
---   >         [whamlet|
---   >             $forall thing <- things
---   >                 ^{showThing thing}
---   >
---   >             <div .pagination>
---   >                  ^{widget}
---   >             |]
---
-paginate :: Int -- ^ items per page
-         -> [a] -- ^ complete list of items
-         -> GHandler s m ([a], GWidget s m ())
+paginate :: Int -> [a] -> GHandler s m ([a], GWidget s m ())
 paginate per items = do
     p <- getCurrentPage
 
@@ -60,25 +67,10 @@
 
     return (xs, paginationWidget p per tot)
 
--- | Paginate directly out of the database.
---
---   > getSomeRoute something = do
---   >     -- note: things is [Entity val] just like selectList returns
---   >     (things, widget) <- runDB $ selectPaginated 10 [SomeThing ==. something] []
---   >
---   >     defaultLayout $ do
---   >         [whamlet|
---   >             $forall thing <- things
---   >                 ^{showThing $ entityVal thing}
---   >
---   >             <div .pagination>
---   >                  ^{widget}
---   >             |]
---
 selectPaginated :: ( MonadTrans (PersistEntityBackend v)
                    , PersistEntity v
                    , PersistQuery (PersistEntityBackend v) (GHandler s m))
-                => Int-> [Filter v] -> [SelectOpt v]
+                => Int -> [Filter v] -> [SelectOpt v]
                 -> PersistEntityBackend v (GHandler s m) ([Entity v], GWidget s1 m1 ())
 selectPaginated per filters selectOpts = do
     p   <- lift getCurrentPage
@@ -86,85 +78,3 @@
     xs  <- selectList filters (selectOpts ++ [OffsetBy ((p-1)*per), LimitTo per])
 
     return (xs, paginationWidget p per tot)
-
--- | A widget showing pagination links. Follows bootstrap principles.
---   Utilizes a \"p\" GET param but leaves all other GET params intact.
-paginationWidget :: Int -- ^ current page
-                 -> Int -- ^ items per page
-                 -> Int -- ^ total number of items
-                 -> GWidget s m ()
-paginationWidget page per tot = do
-    -- total / per + 1 for any remainder
-    let pages = (\(n, r) -> n + (min r 1)) $ tot `divMod` per
-
-    when (pages > 1) $ do
-        let prev = [1       ..(page-1)]
-        let next = [(page+1)..pages   ]
-
-        let lim = 9 -- don't show more than nine links on either side
-        let prev' = if length prev > lim then drop ((length prev) - lim) prev else prev
-        let next' = if length next > lim then take lim next else next
-
-        curParams <- lift $ fmap reqGetParams getRequest
-
-        [whamlet|
-            <ul>
-                ^{linkToDisabled (null prev) curParams (page - 1) "← Previous"}
-
-                $if (/=) prev prev'
-                    <li>^{linkTo curParams 1 "1"}
-                    <li>
-                        <a>...
-
-                $forall p <- prev'
-                    <li>^{linkTo curParams p (show p)}
-
-                <li .active>
-                    <a>#{show page}
-
-                $forall n <- next'
-                    <li>^{linkTo curParams n (show n)}
-
-                $if (/=) next next'
-                    <li>
-                        <a>...
-                    <li>^{linkTo curParams pages (show pages)}
-
-                ^{linkToDisabled (null next) curParams (page + 1) "Next →"}
-            |]
-
--- | looks up the \"p\" GET param and converts it to an Int. returns a
---   default of 1 when conversion fails.
-getCurrentPage :: GHandler s m Int
-getCurrentPage = fmap (fromMaybe 1 . go) $ lookupGetParam "p"
-
-    where
-        go :: Maybe Text -> Maybe Int
-        go mp = readIntegral . T.unpack =<< mp
-
-updateGetParam :: [(Text,Text)] -> (Text,Text) -> Text
-updateGetParam getParams (p, n) = (T.cons '?') . T.intercalate "&"
-                                . map (\(k,v) -> k `T.append` "=" `T.append` v)
-                                . (++ [(p, n)]) . filter ((/= p) . fst) $ getParams
-
-linkTo :: [(Text,Text)] -> Int -> String -> GWidget s m ()
-linkTo params pg txt = do
-    let param = ("p", T.pack $ show pg)
-
-    [whamlet|
-        <a href="#{updateGetParam params param}">#{txt}
-        |]
-
--- | Similiar, but used for Previous/Next so that there's no href when
---   disabled
-linkToDisabled :: Bool -- ^ disabled?
-               -> [(Text,Text)] -> Int -> String -> GWidget s m ()
-linkToDisabled True _ _ txt = [whamlet|
-    <li .prev .disabled>
-        <a>#{txt}
-    |]
-
-linkToDisabled _ params pg txt = [whamlet|
-    <li .prev>
-        ^{linkTo params pg txt}
-    |]
diff --git a/Yesod/Paginator/Widget.hs b/Yesod/Paginator/Widget.hs
new file mode 100644
--- /dev/null
+++ b/Yesod/Paginator/Widget.hs
@@ -0,0 +1,112 @@
+{-# LANGUAGE QuasiQuotes       #-}
+{-# LANGUAGE OverloadedStrings #-}
+module Yesod.Paginator.Widget
+ ( getCurrentPage  
+ , paginationWidget
+ ) where
+
+import Yesod
+
+import Control.Monad (when)
+import Data.Maybe    (fromMaybe)
+import Data.Text     (Text)
+import qualified Data.Text as T
+
+-- | Individual links to pages need to follow strict (but sane) markup
+--   to be styled correctly by bootstrap. This type allows construction
+--   of such links in both enabled and disabled states.
+data PageLink = Enabled Int String String -- ^ page, content, class
+              | Disabled    String String -- ^ content, class
+
+-- | Correctly show one of the constructed links
+showLink :: [(Text, Text)] -> PageLink -> GWidget s m ()
+showLink params (Enabled pg cnt cls) = do
+    let param = ("p", T.pack . show $ pg)
+
+    [whamlet|
+        <li .#{cls}>
+            <a href="#{updateGetParam params param}">#{cnt}
+        |]
+
+    where
+        updateGetParam :: [(Text,Text)] -> (Text,Text) -> Text
+        updateGetParam getParams (p, n) = (T.cons '?') . T.intercalate "&"
+                                        . map (\(k,v) -> k `T.append` "=" `T.append` v)
+                                        . (++ [(p, n)]) . filter ((/= p) . fst) $ getParams
+
+showLink _ (Disabled cnt cls) =
+    [whamlet|
+        <li .#{cls} .disabled>
+            <a>#{cnt}
+        |]
+
+-- | A widget showing pagination links. Follows bootstrap principles.
+--   Utilizes a \"p\" GET param but leaves all other GET params intact.
+paginationWidget :: Int -- ^ current page
+                 -> Int -- ^ items per page
+                 -> Int -- ^ total number of items
+                 -> GWidget s m ()
+paginationWidget page per tot = do
+    -- total / per + 1 for any remainder
+    let pages = (\(n, r) -> n + (min r 1)) $ tot `divMod` per
+
+    when (pages > 1) $ do
+        curParams <- lift $ fmap reqGetParams getRequest
+
+        [whamlet|
+            <ul>
+                $forall link <- buildLinks page pages
+                    ^{showLink curParams link}
+            |]
+
+    where
+        -- | Build up each component of the overall list of links. We'll
+        --   use empty lists to denote ommissions along the way then
+        --   concatenate.
+        buildLinks :: Int -> Int -> [PageLink]
+        buildLinks pg pgs =
+            let prev = [1      .. pg - 1]
+                next = [pg + 1 .. pgs   ]
+
+                -- these always appear
+                prevLink = [(if null prev then Disabled else Enabled (pg - 1)) "«" "prev"]
+                nextLink = [(if null next then Disabled else Enabled (pg + 1)) "»" "next"]
+
+                -- show first/last unless we're on it
+                firstLink = [ Enabled 1   "1"        "prev" | pg > 1   ]
+                lastLink  = [ Enabled pgs (show pgs) "next" | pg < pgs ]
+
+                lim = 9
+
+                -- we'll show ellipsis if there are enough links that some will
+                -- be ommitted from the list
+                prevEllipsis = [ Disabled "..." "prev" | length prev > lim + 1 ]
+                nextEllipsis = [ Disabled "..." "next" | length next > lim + 1 ]
+
+                -- the middle lists, strip the first/last pages and
+                -- correctly take up to limit away from current
+                prevLinks = reverse . take lim . reverse . drop 1 $ map (\p -> Enabled p (show p) "prev") prev
+                nextLinks = take lim . reverse . drop 1 . reverse $ map (\p -> Enabled p (show p) "next") next
+
+                -- finally, this page itself
+                curLink = [Disabled (show pg) "active"]
+
+            in concat [ prevLink
+                      , firstLink
+                      , prevEllipsis
+                      , prevLinks
+                      , curLink
+                      , nextLinks
+                      , nextEllipsis
+                      , lastLink
+                      , nextLink
+                      ]
+
+-- | looks up the \"p\" GET param and converts it to an Int. returns a
+--   default of 1 when conversion fails.
+getCurrentPage :: GHandler s m Int
+getCurrentPage = fmap (fromMaybe 1 . go) $ lookupGetParam "p"
+
+    where
+        go :: Maybe Text -> Maybe Int
+        go mp = readIntegral . T.unpack =<< mp
diff --git a/yesod-paginator.cabal b/yesod-paginator.cabal
--- a/yesod-paginator.cabal
+++ b/yesod-paginator.cabal
@@ -1,5 +1,5 @@
 name:                yesod-paginator
-version:             0.2.1
+version:             0.2.2.1
 synopsis:            A pagination approach for yesod
 description:         Paginate a list showing a per-item widget and links to other pages
 homepage:            http://github.com/pbrisbin/yesod-paginator
@@ -13,6 +13,7 @@
 
 library
   exposed-modules: Yesod.Paginator
+                   Yesod.Paginator.Widget
 
   build-depends: base       >= 4    && < 5
                , text       >= 0.11 && < 0.12
