diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,6 +1,10 @@
 # ChangeLog for yesod-core
 
-## 1.6.28.0 (unreleased)
+## 1.6.28.1
+
+* Add type arguments to the sub routes [#1866](https://github.com/yesodweb/yesod/pull/1866)
+
+## 1.6.28.0 
 
 * Allow users to control generation of the `resourcesSite :: [ResourceTree String]` value. [#1881](https://github.com/yesodweb/yesod/pull/1881)
 
diff --git a/bench/widget.hs b/bench/widget.hs
--- a/bench/widget.hs
+++ b/bench/widget.hs
@@ -63,6 +63,6 @@
     -}
 
 bigTableBlaze :: Show a => [[a]] -> Int64
-bigTableBlaze t = L.length $ Utf8.renderHtml $ table $ concatMap row t
+bigTableBlaze t = L.length $ Utf8.renderHtml $ table $ foldMap row t
   where
-    row r = tr $ concatMap (td . toHtml . show) r
+    row r = tr $ foldMap (td . toHtml . show) r
diff --git a/src/Yesod/Core/Dispatch.hs b/src/Yesod/Core/Dispatch.hs
--- a/src/Yesod/Core/Dispatch.hs
+++ b/src/Yesod/Core/Dispatch.hs
@@ -29,6 +29,7 @@
     , setShowDerived
     , setReadDerived
     , setCreateResources
+    , setParameterizedSubroute
       -- *** Helpers
     , defaultGen
     , getGetMaxExpires
diff --git a/src/Yesod/Core/Internal/TH.hs b/src/Yesod/Core/Internal/TH.hs
--- a/src/Yesod/Core/Internal/TH.hs
+++ b/src/Yesod/Core/Internal/TH.hs
@@ -43,6 +43,7 @@
     , setShowDerived
     , setReadDerived
     , setCreateResources
+    , setParameterizedSubroute
     )
  where
 
@@ -223,7 +224,7 @@
     let appCxt = fmap (\ctxs ->
             case ctxs of
                 c:rest ->
-                    foldl' (\acc v -> acc `AppT` nameToType v) (ConT $ mkName c) rest
+                    foldl' (\acc v -> acc `AppT` fst (nameToType v)) (ConT $ mkName c) rest
                 [] -> error $ "Bad context: " ++ show ctxs
           ) appCxt'
     mname <- lookupTypeName namestr
@@ -244,13 +245,14 @@
     -- Generate as many variable names as the arity indicates
     vns <- replicateM (arity - length mtys) $ newName "t"
     -- types that you apply to get a concrete site name
-    let argtypes = fmap nameToType mtys ++ fmap VarT vns
+    let boundNames = fmap nameToType mtys
+        argtypes = fmap fst boundNames ++ fmap VarT vns
     -- typevars that should appear in synonym head
     let argvars = (fmap mkName . filter isTvar) mtys ++ vns
         -- Base type (site type with variables)
     let site = foldl' AppT (ConT name) argtypes
         res = map (fmap (parseType . dropBracket)) resS
-    renderRouteDec <- mkRenderRouteInstanceOpts opts appCxt site res
+    renderRouteDec <- mkRenderRouteInstanceOpts opts appCxt boundNames site res
     routeAttrsDec  <- mkRouteAttrsInstance appCxt site res
     dispatchDec    <- mkDispatchInstance site appCxt f res
     parseRoute <- mkParseRouteInstance appCxt site res
diff --git a/src/Yesod/Routes/Parse.hs b/src/Yesod/Routes/Parse.hs
--- a/src/Yesod/Routes/Parse.hs
+++ b/src/Yesod/Routes/Parse.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE PatternGuards #-}
 {-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TupleSections #-}
 {-# OPTIONS_GHC -fno-warn-missing-fields #-} -- QuasiQuoter
 
 module Yesod.Routes.Parse
@@ -265,14 +266,15 @@
             gos' (front . (t:)) xs'
 
 ttToType :: TypeTree -> Type
-ttToType (TTTerm s) = nameToType s
+ttToType (TTTerm s) = fst $ nameToType s
 ttToType (TTApp x y) = ttToType x `AppT` ttToType y
 ttToType (TTList t) = ListT `AppT` ttToType t
 
-nameToType :: String -> Type
-nameToType t = if isTvar t
-               then VarT $ mkName t
-               else ConT $ mkName t
+nameToType :: String -> (Type, Name)
+nameToType t = (, nm) $ if isTvar t
+               then VarT nm
+               else ConT nm
+    where nm = mkName t
 
 isTvar :: String -> Bool
 isTvar (h:_) = isLower h
diff --git a/src/Yesod/Routes/TH/RenderRoute.hs b/src/Yesod/Routes/TH/RenderRoute.hs
--- a/src/Yesod/Routes/TH/RenderRoute.hs
+++ b/src/Yesod/Routes/TH/RenderRoute.hs
@@ -2,12 +2,11 @@
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE TemplateHaskellQuotes #-}
 {-# LANGUAGE TupleSections #-}
+{-# LANGUAGE ViewPatterns #-}
 
 module Yesod.Routes.TH.RenderRoute
     ( -- ** RenderRoute
-      mkRenderRouteInstance
-    , mkRenderRouteInstanceOpts
-    , mkRouteCons
+      mkRenderRouteInstanceOpts
     , mkRouteConsOpts
     , mkRenderRouteClauses
     , shouldCreateResources
@@ -18,25 +17,25 @@
     , setShowDerived
     , setReadDerived
     , setCreateResources
+    , setParameterizedSubroute
     ) where
 
 import Yesod.Routes.TH.Types
-import Language.Haskell.TH (conT)
 import Language.Haskell.TH.Syntax
-import Data.Bits (xor)
 import Data.Maybe (maybeToList)
 import Control.Monad (replicateM)
 import Data.Text (pack)
 import Web.PathPieces (PathPiece (..), PathMultiPiece (..))
 import Yesod.Routes.Class
+import Data.Foldable
 
 -- | General opts data type for generating yesod.
 --
 -- Contains options for customizing code generation for the router in
 -- 'mkYesodData', including what type class instances will be derived for
--- the route datatype and whether or not to create the @resources ::
--- [ResourceTree String]@ value. Use the setting functions on `defaultOpts`
--- to set specific fields.
+-- the route datatype, whether to parameterize subroutes,
+-- and whether or not to create the @resources :: [ResourceTree String]@ value.
+-- Use the setting functions on `defaultOpts` to set specific fields.
 --
 -- @since 1.6.25.0
 data RouteOpts = MkRouteOpts
@@ -44,12 +43,13 @@
     , roDerivedShow :: Bool
     , roDerivedRead :: Bool
     , roCreateResources :: Bool
+    , roParameterizedSubroute :: Bool
     }
 
 -- | Default options for generating routes.
 --
--- Defaults to all instances derived and to create the @resourcesSite ::
--- [ResourceTree String]@ term.
+-- Defaults to all instances derived, subroutes being unparameterized, and to
+-- create the @resourcesSite :: [ResourceTree String]@ term.
 --
 -- @since 1.6.25.0
 defaultOpts :: RouteOpts
@@ -58,6 +58,7 @@
     , roDerivedShow = True
     , roDerivedRead = True
     , roCreateResources = True
+    , roParameterizedSubroute = False
     }
 
 -- |
@@ -96,6 +97,12 @@
 shouldCreateResources :: RouteOpts -> Bool
 shouldCreateResources = roCreateResources
 
+-- | If True, we will correctly pass parameters for subroutes around.
+--
+-- @since 1.6.28.0
+setParameterizedSubroute :: Bool -> RouteOpts -> RouteOpts
+setParameterizedSubroute b rdo = rdo { roParameterizedSubroute = b }
+
 -- |
 --
 -- @since 1.6.25.0
@@ -103,23 +110,37 @@
 instanceNamesFromOpts MkRouteOpts {..} = prependIf roDerivedEq ''Eq $ prependIf roDerivedShow ''Show $ prependIf roDerivedRead ''Read []
     where prependIf b = if b then (:) else const id
 
--- |
---
--- @since 1.6.28.0
-
--- | Generate the constructors of a route data type.
-mkRouteCons :: [ResourceTree Type] -> Q ([Con], [Dec])
-mkRouteCons = mkRouteConsOpts defaultOpts
+-- | Nullify the list unless we are using parameterised subroutes.
+nullifyWhenNoParam :: RouteOpts -> [a] -> [a]
+nullifyWhenNoParam opts = if roParameterizedSubroute opts then id else const []
 
 -- | Generate the constructors of a route data type, with custom opts.
 --
 -- @since 1.6.25.0
-mkRouteConsOpts :: RouteOpts -> [ResourceTree Type] -> Q ([Con], [Dec])
-mkRouteConsOpts opts rttypes =
-    mconcat <$> mapM mkRouteCon rttypes
+mkRouteConsOpts :: RouteOpts -> Cxt -> [(Type, Name)] -> [ResourceTree Type] -> ([Con], [Dec])
+mkRouteConsOpts opts cxt (nullifyWhenNoParam opts -> tyargs) =
+    mkRouteConsOpts'
   where
+    -- th-abstraction does cover this but the version it was introduced in
+    -- isn't always available
+    tyvarbndr =
+#if MIN_VERSION_template_haskell(2,21,0)
+        (`PlainTV` BndrReq) :: Name -> TyVarBndr BndrVis
+#elif MIN_VERSION_template_haskell(2,17,0)
+        (`PlainTV` ()) :: Name -> TyVarBndr ()
+#else
+        PlainTV :: Name -> TyVarBndr
+#endif
+
+    subrouteDecTypeArgs = fmap (tyvarbndr . snd) tyargs
+
+    (inlineDerives, mkSds) = getDerivesFor opts (nullifyWhenNoParam opts cxt)
+
+    mkRouteConsOpts' :: [ResourceTree Type] -> ([Con], [Dec])
+    mkRouteConsOpts' = foldMap mkRouteCon
+
     mkRouteCon (ResourceLeaf res) =
-        return ([con], [])
+        ([con], [])
       where
         con = NormalC (mkName $ resourceName res)
             $ map (notStrict,)
@@ -135,24 +156,22 @@
                 Subsite { subsiteType = typ } -> [ConT ''Route `AppT` typ]
                 _ -> []
 
-    mkRouteCon (ResourceParent name _check pieces children) = do
-        (cons, decs) <- mkRouteConsOpts opts children
-        let conts = mapM conT $ instanceNamesFromOpts opts
-#if MIN_VERSION_template_haskell(2,12,0)
-        dec <- DataD [] (mkName name) [] Nothing cons <$> fmap (pure . DerivClause Nothing) conts
-#else
-        dec <- DataD [] (mkName name) [] Nothing cons <$> conts
-#endif
-        return ([con], dec : decs)
+    mkRouteCon (ResourceParent name _check pieces children) =
+        let (cons, decs) = mkRouteConsOpts' children
+            dec = DataD [] dataName subrouteDecTypeArgs Nothing cons inlineDerives
+        in ([con], dec : decs ++ mkSds consDataType)
       where
-        con = NormalC (mkName name)
+        con = NormalC dataName
             $ map (notStrict,)
-            $ singles ++ [ConT $ mkName name]
+            $ singles ++ [consDataType]
 
         singles = concatMap toSingle pieces
         toSingle Static{} = []
         toSingle (Dynamic typ) = [typ]
 
+        dataName = mkName name
+        consDataType = foldl' (\b a -> b `AppT` fst a) (ConT dataName) tyargs
+
 -- | Clauses for the 'renderRoute' method.
 mkRenderRouteClauses :: [ResourceTree Type] -> Q [Clause]
 mkRenderRouteClauses =
@@ -247,43 +266,41 @@
 -- | Generate the 'RenderRoute' instance.
 --
 -- This includes both the 'Route' associated type and the
--- 'renderRoute' method.  This function uses both 'mkRouteCons' and
--- 'mkRenderRouteClasses'.
-mkRenderRouteInstance :: Cxt -> Type -> [ResourceTree Type] -> Q [Dec]
-mkRenderRouteInstance = mkRenderRouteInstanceOpts defaultOpts
-
--- | Generate the 'RenderRoute' instance.
---
--- This includes both the 'Route' associated type and the
--- 'renderRoute' method.  This function uses both 'mkRouteCons' and
--- 'mkRenderRouteClasses'.
+-- 'renderRoute' method.  This function uses both 'mkRouteConsOpts' and
+-- 'mkRenderRouteClauses'.
 --
 -- @since 1.6.25.0
-mkRenderRouteInstanceOpts :: RouteOpts -> Cxt -> Type -> [ResourceTree Type] -> Q [Dec]
-mkRenderRouteInstanceOpts opts cxt typ ress = do
+mkRenderRouteInstanceOpts :: RouteOpts -> Cxt -> [(Type, Name)] -> Type -> [ResourceTree Type] -> Q [Dec]
+mkRenderRouteInstanceOpts opts cxt tyargs typ ress = do
     cls <- mkRenderRouteClauses ress
-    (cons, decs) <- mkRouteConsOpts opts ress
+    let (cons, decs) = mkRouteConsOpts opts cxt tyargs ress
+    let did = DataInstD []
 #if MIN_VERSION_template_haskell(2,15,0)
-    did <- DataInstD [] Nothing (AppT (ConT ''Route) typ) Nothing cons <$> fmap (pure . DerivClause Nothing) (mapM conT (clazzes False))
-    let sds = fmap (\t -> StandaloneDerivD Nothing cxt $ ConT t `AppT` ( ConT ''Route `AppT` typ)) (clazzes True)
-#elif MIN_VERSION_template_haskell(2,12,0)
-    did <- DataInstD [] ''Route [typ] Nothing cons <$> fmap (pure . DerivClause Nothing) (mapM conT (clazzes False))
-    let sds = fmap (\t -> StandaloneDerivD Nothing cxt $ ConT t `AppT` ( ConT ''Route `AppT` typ)) (clazzes True)
+            Nothing routeDataName
 #else
-    did <- DataInstD [] ''Route [typ] Nothing cons <$> mapM conT (clazzes False)
-    let sds = fmap (\t -> StandaloneDerivD cxt $ ConT t `AppT` ( ConT ''Route `AppT` typ)) (clazzes True)
+            ''Route [typ]
 #endif
+            Nothing cons inlineDerives
     return $ instanceD cxt (ConT ''RenderRoute `AppT` typ)
         [ did
         , FunD (mkName "renderRoute") cls
         ]
-        : sds ++ decs
+        : mkSds routeDataName ++ decs
   where
-    clazzes standalone = if standalone `xor` null cxt then
-          clazzes'
-        else
-          []
-    clazzes' = instanceNamesFromOpts opts
+    routeDataName = ConT ''Route `AppT` typ
+    (inlineDerives, mkSds) = getDerivesFor opts cxt
+
+-- | Get the simple derivation clauses and the standalone derivation clauses
+-- for a given type and context.
+--
+-- If there are any additional classes needed for context, we just produce standalone
+-- clauses. Else, we produce basic deriving clauses for a declaration.
+getDerivesFor :: RouteOpts -> Cxt -> ([DerivClause], Type ->  [Dec])
+getDerivesFor opts cxt
+    | null cxt = ([DerivClause Nothing clazzes'], const [])
+    | otherwise = ([], \typ -> fmap (StandaloneDerivD Nothing cxt . (`AppT` typ)) clazzes')
+    where
+    clazzes' = ConT <$> instanceNamesFromOpts opts
 
 notStrict :: Bang
 notStrict = Bang NoSourceUnpackedness NoSourceStrictness
diff --git a/test/Hierarchy.hs b/test/Hierarchy.hs
--- a/test/Hierarchy.hs
+++ b/test/Hierarchy.hs
@@ -114,7 +114,7 @@
 --  /#Int TrailingIntR GET
 |]
 
-    rrinst <- mkRenderRouteInstance [] (ConT ''Hierarchy) $ map (fmap parseType) resources
+    rrinst <- mkRenderRouteInstanceOpts defaultOpts [] [] (ConT ''Hierarchy) $ map (fmap parseType) resources
     rainst <- mkRouteAttrsInstance [] (ConT ''Hierarchy) $ map (fmap parseType) resources
     prinst <- mkParseRouteInstance [] (ConT ''Hierarchy) $ map (fmap parseType) resources
     dispatch <- mkDispatchClause MkDispatchSettings
@@ -130,9 +130,7 @@
         } resources
     return $
         InstanceD
-#if MIN_VERSION_template_haskell(2,11,0)
             Nothing
-#endif
             []
             (ConT ''Dispatcher
                 `AppT` ConT ''Hierarchy
diff --git a/test/RouteSpec.hs b/test/RouteSpec.hs
--- a/test/RouteSpec.hs
+++ b/test/RouteSpec.hs
@@ -73,7 +73,7 @@
             [ ResourceLeaf $ Resource "ChildR" [] (Methods Nothing ["GET"]) ["child"] True
             ]
         ress = resParent : resLeaves
-    rrinst <- mkRenderRouteInstance [] (ConT ''MyApp) ress
+    rrinst <- mkRenderRouteInstanceOpts defaultOpts [] [] (ConT ''MyApp) ress
     rainst <- mkRouteAttrsInstance [] (ConT ''MyApp) ress
     prinst <- mkParseRouteInstance [] (ConT ''MyApp) ress
     dispatch <- mkDispatchClause MkDispatchSettings
@@ -89,9 +89,7 @@
         } ress
     return $
         InstanceD
-#if MIN_VERSION_template_haskell(2,11,0)
             Nothing
-#endif
             []
             (ConT ''Dispatcher
                 `AppT` ConT ''MyApp
diff --git a/test/YesodCoreTest/ParameterizedSite.hs b/test/YesodCoreTest/ParameterizedSite.hs
--- a/test/YesodCoreTest/ParameterizedSite.hs
+++ b/test/YesodCoreTest/ParameterizedSite.hs
@@ -5,14 +5,19 @@
     ) where
 
 import Data.ByteString.Lazy (ByteString)
-import Network.Wai.Test (runSession, request, defaultRequest, assertBodyContains)
+import Network.Wai.Test (runSession, request, defaultRequest, assertBodyContains, setPath)
 import Test.Hspec (Spec, describe, it)
-import Yesod.Core (YesodDispatch)
+import Yesod.Core (YesodDispatch, renderRoute, Route)
 import Yesod.Core.Dispatch (toWaiApp)
+import Data.Traversable
+import Data.Foldable
+import Data.Text.Encoding
+import Data.Text as T
 
 import YesodCoreTest.ParameterizedSite.PolyAny (PolyAny (..))
 import YesodCoreTest.ParameterizedSite.PolyShow (PolyShow (..))
 import YesodCoreTest.ParameterizedSite.Compat (Compat (..))
+import qualified YesodCoreTest.ParameterizedSite.SubRoute as SR
 
 -- These are actually tests for template haskell. So if it compiles, it works
 parameterizedSiteTest :: Spec
@@ -20,6 +25,7 @@
     it "Polymorphic unconstrained stub" $ runStub (PolyAny ())
     it "Polymorphic stub with Show" $ runStub' "1337" (PolyShow (1337 :: Int))
     it "Polymorphic unconstrained stub, old-style" $ runStub (Compat () ())
+    it "Polymorphic stub, sub routes" $ runStubAgainst [(SR.HomeR 456, ["Stub", "123", "456"]), (SR.EditorR (SR.AwayR 789), ["Stub", "123", "789"])] (SR.SubRoute (123 :: Int))
 
 runStub :: YesodDispatch a => a -> IO ()
 runStub stub =
@@ -35,4 +41,15 @@
             res <- request defaultRequest
             assertBodyContains "Stub" res
             assertBodyContains body res
+    in toWaiApp stub >>= runSession actions
+
+runStubAgainst :: YesodDispatch a => [(Route a, [ByteString])] -> a -> IO ()
+runStubAgainst urlBodies stub =
+    let actions = do
+            for_ urlBodies $ \(url, bodies) -> do
+                let (nonQuery, _query) = renderRoute url
+                let render = T.intercalate "/" nonQuery
+                let req = setPath defaultRequest (encodeUtf8 render)
+                res <- request req
+                for bodies (`assertBodyContains` res)
     in toWaiApp stub >>= runSession actions
diff --git a/test/YesodCoreTest/ParameterizedSite/SubRoute.hs b/test/YesodCoreTest/ParameterizedSite/SubRoute.hs
new file mode 100644
--- /dev/null
+++ b/test/YesodCoreTest/ParameterizedSite/SubRoute.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE
+    TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses
+  , OverloadedStrings, StandaloneDeriving, FlexibleInstances, FlexibleContexts
+  , ViewPatterns, UndecidableInstances, ConstraintKinds
+  #-}
+module YesodCoreTest.ParameterizedSite.SubRoute where
+
+import Yesod.Core
+import Data.Kind (Type)
+
+type Constraints p = (Eq (Key p), Show (Key p), Read (Key p), PathPiece (Key p), Show p)
+
+class Constraints p => SiteClass p where
+  type Key p :: Type
+
+newtype SubRoute a v = SubRoute a
+  deriving (Eq, Show, Read)
+
+instance SiteClass Int where
+  type Key Int = Int
+
+mkYesodOpts (setParameterizedSubroute True defaultOpts) "(SiteClass p) => SubRoute p v" [parseRoutes|
+/home/#{Key p} HomeR GET
+/editor EditorR:
+  /away/#{Key p} AwayR GET
+|]
+
+{-
+The above generates data structures and instances like the following:
+
+  data Route (SubRoute p v) = HomeR (Key p) | EditorR (EditorR p v)
+
+deriving instance SiteClass p => Eq (Route (SubRoute p v))
+deriving instance SiteClass p => Show (Route (SubRoute p v))
+deriving instance SiteClass p => Read (Route (SubRoute p v))
+data EditorR p v = AwayR (Key p)
+deriving instance SiteClass p => Eq (EditorR p v)
+deriving instance SiteClass p => Show (EditorR p v)
+deriving instance SiteClass p => Read (EditorR p v)
+
+Note that `p` is now threaded through the other data structures.
+
+Otherwise, EditorR's definition would've been:
+data EditorR
+  = AwayR (Key p)
+  deriving (Eq, Show, Read)
+which clearly doesn't work, as `p` is not in scope.
+-}
+
+instance SiteClass a => Yesod (SubRoute a v)
+
+getHomeR :: SiteClass a => Key a -> HandlerFor (SubRoute a v) Html
+getHomeR key = do
+    SubRoute x <- liftHandler getYesod
+    defaultLayout
+        [whamlet|
+            <p>
+                Stub #{show x} #{show key}
+        |]
+
+getAwayR :: SiteClass a => Key a -> HandlerFor (SubRoute a v) Html
+getAwayR key1 = do
+    SubRoute x <- liftHandler getYesod
+    defaultLayout
+        [whamlet|
+            <p>
+                Stub #{show x} #{show key1}
+        |]
diff --git a/yesod-core.cabal b/yesod-core.cabal
--- a/yesod-core.cabal
+++ b/yesod-core.cabal
@@ -1,5 +1,5 @@
 name:            yesod-core
-version:         1.6.28.0
+version:         1.6.28.1
 license:         MIT
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -53,7 +53,7 @@
                    , random                >= 1.0.0.2  && < 1.4
                    , resourcet             >= 1.2
                    , shakespeare           >= 2.0
-                   , template-haskell      >= 2.11
+                   , template-haskell      >= 2.13
                    , text                  >= 0.7
                    , time                  >= 1.5
                    , transformers          >= 0.4
@@ -134,6 +134,7 @@
                  , random
                  , path-pieces
                  , HUnit
+                 , th-abstraction
 
 test-suite tests
     default-language: Haskell2010
@@ -167,6 +168,7 @@
                    YesodCoreTest.ParameterizedSite.Compat
                    YesodCoreTest.ParameterizedSite.PolyAny
                    YesodCoreTest.ParameterizedSite.PolyShow
+                   YesodCoreTest.ParameterizedSite.SubRoute
                    YesodCoreTest.RawResponse
                    YesodCoreTest.Redirect
                    YesodCoreTest.Reps
