diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2014, Luke Randall
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Luke Randall nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# snap-web-routes: Type safe URLs for Snap
+
+`snap-web-routes` provides type safe URLs for Snap using [`web routes`](http://hackage.haskell.org/package/web-routes).
+
+# How to use
+
+See `Snap.Web.Routes` for instructions.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/snap-web-routes.cabal b/snap-web-routes.cabal
new file mode 100644
--- /dev/null
+++ b/snap-web-routes.cabal
@@ -0,0 +1,35 @@
+name:                snap-web-routes
+version:             0.1.0.0
+synopsis:            Type safe URLs for Snap
+description:         Type safe URL generation and routing for Snap using web-routes
+homepage:            http://github.com/lukerandall/snap-web-routes
+license:             BSD3
+license-file:        LICENSE
+author:              Luke Randall
+maintainer:          luke.randall@gmail.com
+category:            Web, Snap
+build-type:          Simple
+cabal-version:       >=1.10
+
+extra-source-files:
+  LICENSE,
+  README.md
+
+library
+  hs-source-dirs:      src
+
+  exposed-modules:
+    Snap.Web.Routes
+    Snap.Web.Routes.Heist
+
+  build-depends:
+    base                      >= 4.4     && < 5,
+    heist                     >= 0.13    && < 0.14,
+    mtl                       >= 2       && < 3,
+    snap-core                 >= 0.9     && < 0.11,
+    snap                      >= 0.13    && < 0.14,
+    text                      >= 0.11    && < 1.2,
+    web-routes                >= 0.27    && < 0.28,
+    xmlhtml                   >= 0.1
+
+  default-language:    Haskell2010
diff --git a/src/Snap/Web/Routes.hs b/src/Snap/Web/Routes.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Web/Routes.hs
@@ -0,0 +1,129 @@
+{-|
+
+This module provides a ready to use implementation of `web-routes` for Snap.
+
+To get going, you'll need to add a few things to `Application.hs`.
+
+> {-# LANGUAGE DeriveGeneric     #-}
+> {-# LANGUAGE FlexibleInstances #-}
+> {-# LANGUAGE TypeFamilies      #-}
+
+DeriveGeneric is used to derive the `PathInfo` instance for your URL data type,
+the rest are needed by `web-routes`.
+
+> import Data.Text (Text)
+> import Snap.Web.Routes
+
+"Snap.Web.Routes" exports the data types needed to define your `PathInfo` and
+`MonadRoute` instances below.
+
+> data AppUrl
+>     = Count Int
+>     | Echo Text
+>     | Paths [Text]
+>       deriving (Generic)
+
+Define your application's URL data type. Deriving a `Generic` instance gives
+you a `PathInfo` instance for free.
+
+> data App = App
+>     { _routeFn :: AppUrl -> [(Text, Maybe Text)] -> Text
+>     }
+
+Extend your App type to include a routing function.
+
+> instance PathInfo AppUrl
+
+Get your free PathInfo instance. Alternatives are to use `web-routes-th` or
+implement PathInfo yourself.
+
+> instance MonadRoute (Handler App App) where
+>    type URL (Handler App App) = AppUrl
+>    askRouteFn = gets _routeFn
+
+Define your MonadRoute instance. In particular, `type URL (Handler App App)`
+must be set to your URL data type defined above and `askRouteFn` should point
+to the routing function you added to your App type.
+
+Moving on to `Site.hs`.
+
+> import Snap.Web.Routes
+
+Snap.Web.Routes provides a convenience router function you'll need.
+
+> routes :: [(ByteString, Handler App App ())]
+> routes = [ ("",          serveDirectory "static")
+>          , ("",          routeWith routeAppUrl)
+>          ]
+
+Add your routes to the bottom of the routes list using routeWith.
+
+> routeAppUrl :: AppUrl -> Handler App App ()
+> routeAppUrl appUrl =
+>     case appUrl of
+>       (Count n)   -> writeText $ ("Count = " `T.append` (T.pack $ show n))
+>       (Echo text) -> echo text
+>       (Paths ps)  -> writeText $ T.intercalate " " ps
+
+> echo :: T.Text -> Handler App App ()
+> echo msg = heistLocal (bindString "message" msg) $ render "echo"
+
+Define the handler for each data constructor in your URL data type.
+
+> app :: SnapletInit App App
+> app = makeSnaplet "app" "An example application with snap-web-routes." Nothing $ do
+>     addRoutes routes
+>     return $ App renderRoute
+
+Lastly, add the routing function to your app. If you prefixed the routes in routeWith:
+
+>          , ("/prefix",          routeWith routeAppUrl)
+
+then use `renderRouteWithPrefix` instead:
+
+>     return . App $ renderRouteWithPrefix "/prefix"
+
+|-}
+
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies      #-}
+
+module Snap.Web.Routes
+  ( renderRoute
+  , renderRouteWithPrefix
+  , routeWith
+  , heistUrl
+  , gets
+  , Generic
+  , MonadRoute(..)
+  , PathInfo(..)
+  ) where
+
+import Control.Monad.State (lift, gets)
+import Data.Text (Text, append, pack)
+import Heist (HeistT)
+import Snap.Core
+import Snap.Web.Routes.Heist
+import Snap.Snaplet
+import Web.Routes
+
+
+instance (MonadRoute m) => MonadRoute (HeistT n m) where
+    type URL (HeistT n m) = URL m
+    askRouteFn = lift askRouteFn
+
+
+routeWith :: (PathInfo url, MonadSnap m) => (url -> m ()) -> m ()
+routeWith router =
+    do rq <- getRequest
+       case fromPathInfo $ rqPathInfo rq of
+         (Left e) -> writeText (pack e)
+         (Right url) -> router url
+
+
+renderRoute :: PathInfo url => url -> [(Text, Maybe Text)] -> Text
+renderRoute = renderRouteWithPrefix ""
+
+
+renderRouteWithPrefix :: PathInfo url => Text -> url -> [(Text, Maybe Text)] -> Text
+renderRouteWithPrefix prefix url params = prefix `append` toPathInfoParams url params
diff --git a/src/Snap/Web/Routes/Heist.hs b/src/Snap/Web/Routes/Heist.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Web/Routes/Heist.hs
@@ -0,0 +1,12 @@
+module Snap.Web.Routes.Heist
+  ( heistUrl
+  ) where
+
+import Text.XmlHtml hiding (render)
+import Web.Routes
+
+
+heistUrl :: MonadRoute m => URL m -> m [Node]
+heistUrl u =
+    do t <- showURL u
+       return [TextNode t]
