diff --git a/digestive-functors-heist.cabal b/digestive-functors-heist.cabal
--- a/digestive-functors-heist.cabal
+++ b/digestive-functors-heist.cabal
@@ -1,5 +1,5 @@
 Name:          digestive-functors-heist
-Version:       0.3.1.0
+Version:       0.4.0.0
 Synopsis:      Heist frontend for the digestive-functors library
 Description:   Heist frontend for the digestive-functors library
 Homepage:      http://github.com/jaspervdj/digestive-functors
@@ -17,8 +17,8 @@
   Exposed-modules: Text.Digestive.Heist
 
   Build-depends:
-    base               >= 4       && < 5,
-    digestive-functors >= 0.3.0.1 && < 0.4,
-    heist              >= 0.8     && < 0.9,
-    text               >= 0.11    && < 0.12,
-    xmlhtml            >= 0.1     && < 0.2
+    base               >= 4    && < 5,
+    digestive-functors >= 0.4  && < 0.5,
+    heist              >= 0.8  && < 0.9,
+    text               >= 0.11 && < 0.12,
+    xmlhtml            >= 0.1  && < 0.3
diff --git a/src/Text/Digestive/Heist.hs b/src/Text/Digestive/Heist.hs
--- a/src/Text/Digestive/Heist.hs
+++ b/src/Text/Digestive/Heist.hs
@@ -28,6 +28,7 @@
     , bindDigestiveSplices
 
       -- * Main splices
+    , dfInput
     , dfInputText
     , dfInputTextArea
     , dfInputPassword
@@ -47,6 +48,8 @@
     ) where
 
 import Control.Monad (liftM, mplus)
+import Data.Function (on)
+import Data.List (unionBy)
 import Data.Maybe (fromMaybe)
 import Data.Monoid (mappend)
 
@@ -61,7 +64,8 @@
 
 digestiveSplices :: Monad m => View Text -> [(Text, Splice m)]
 digestiveSplices view =
-    [ ("dfInputText",      dfInputText view)
+    [ ("dfInput",          dfInput view)
+    , ("dfInputText",      dfInputText view)
     , ("dfInputTextArea",  dfInputTextArea view)
     , ("dfInputPassword",  dfInputPassword view)
     , ("dfInputHidden",    dfInputHidden view)
@@ -100,6 +104,23 @@
 getContent :: Monad m => HeistT m [X.Node]
 getContent = liftM X.childNodes getParamNode
 
+-- | Does not override existing attributes
+addAttrs :: [(Text, Text)]  -- ^ Original attributes
+         -> [(Text, Text)]  -- ^ Attributes to add
+         -> [(Text, Text)]  -- ^ Resulting attributes
+addAttrs = unionBy (on (==) fst)
+
+-- | Generate an input field with a supplied type. Example:
+--
+-- > <dfInput type="date" ref="date" />
+dfInput :: Monad m => View v -> Splice m
+dfInput view = do
+    (ref, attrs) <- getRefAttributes Nothing
+    let ref'  = absoluteRef ref view
+        value = fieldInputText ref view
+    return $ makeElement "input" [] $ addAttrs attrs
+        [("id", ref'), ("name", ref'), ("value", value)]
+
 -- | Generate a text input field. Example:
 --
 -- > <dfInputText ref="user.name" />
@@ -108,9 +129,8 @@
     (ref, attrs) <- getRefAttributes Nothing
     let ref'  = absoluteRef ref view
         value = fieldInputText ref view
-    return $ makeElement "input" [] $
-        ("type", "text") : ("id", ref') :
-        ("name", ref') : ("value", value) : attrs
+    return $ makeElement "input" [] $ addAttrs attrs
+        [("type", "text"), ("id", ref'), ("name", ref'), ("value", value)]
 
 -- | Generate a text area. Example:
 --
@@ -120,8 +140,8 @@
     (ref, attrs) <- getRefAttributes Nothing
     let ref'  = absoluteRef ref view
         value = fieldInputText ref view
-    return $ makeElement "textarea" [X.TextNode value] $
-        ("id", ref') : ("name", ref') : attrs
+    return $ makeElement "textarea" [X.TextNode value] $ addAttrs attrs
+        [("id", ref'), ("name", ref')]
 
 -- | Generate a password field. Example:
 --
@@ -131,9 +151,8 @@
     (ref, attrs) <- getRefAttributes Nothing
     let ref'  = absoluteRef ref view
         value = fieldInputText ref view
-    return $ makeElement "input" [] $
-        ("type", "password") : ("id", ref') :
-        ("name", ref') : ("value", value) : attrs
+    return $ makeElement "input" [] $ addAttrs attrs
+        [("type", "password"), ("id", ref'), ("name", ref'), ("value", value)]
 
 -- | Generate a hidden input field. Example:
 --
@@ -143,9 +162,8 @@
     (ref, attrs) <- getRefAttributes Nothing
     let ref'  = absoluteRef ref view
         value = fieldInputText ref view
-    return $ makeElement "input" [] $
-        ("type", "hidden") : ("id", ref') :
-        ("name", ref') : ("value", value) : attrs
+    return $ makeElement "input" [] $ addAttrs attrs
+        [("type", "hidden"), ("id", ref'), ("name", ref'), ("value", value)]
 
 -- | Generate a select button (also known as a combo box). Example:
 --
@@ -160,8 +178,8 @@
         makeOption c i = X.Element "option"
             (attr (idx == i) ("selected", "selected") [("value", value i)])
             [X.TextNode c]
-    return $ makeElement "select" children $
-        ("id", ref') : ("name", ref') : attrs
+    return $ makeElement "select" children $ addAttrs attrs
+        [("id", ref'), ("name", ref')]
 
 -- | Generate a number of radio buttons. Example:
 --
@@ -177,10 +195,10 @@
         value i        = ref' `mappend` "." `mappend` T.pack (show i)
         makeOption c i =
             [ X.Element "input"
-                (attr (idx == i) ("checked", "checked") $
-                    ("type", "radio") : ("value", value i) :
-                    ("id",    value i) : ("name", ref') :
-                    attrs ) []
+                (attr (idx == i) ("checked", "checked") $ addAttrs attrs
+                    [ ("type", "radio"), ("value", value i)
+                    , ("id", value i), ("name", ref')
+                    ]) []
             , X.Element "label" [("for", value i)] [X.TextNode c]
             ]
 
@@ -194,8 +212,9 @@
     (ref, attrs) <- getRefAttributes Nothing
     let ref'  = absoluteRef ref view
         value = fieldInputBool ref view
-    return $ makeElement "input" [] $ attr value ("checked", "checked") $
-        ("type", "checkbox") : ("id", ref') : ("name", ref') : attrs
+    return $ makeElement "input" [] $ addAttrs attrs $
+        attr value ("checked", "checked") $
+        [("type", "checkbox"), ("id", ref'), ("name", ref')]
 
 -- | Generate a file upload element. Example:
 --
@@ -205,9 +224,8 @@
     (ref, attrs) <- getRefAttributes Nothing
     let ref'  = absoluteRef ref view
         value = maybe "" T.pack $ fieldInputFile ref view
-    return $ makeElement "input" [] $
-        ("type", "file") : ("id", ref') :
-        ("name", ref') : ("value", value) : attrs
+    return $ makeElement "input" [] $ addAttrs attrs
+        [("type", "file"), ("id", ref'), ("name", ref'), ("value", value)]
 
 -- | Generate a submit button. Example:
 --
@@ -215,7 +233,7 @@
 dfInputSubmit :: Monad m => View v -> Splice m
 dfInputSubmit _ = do
     (_, attrs) <- getRefAttributes Nothing
-    return $ makeElement "input" [] $ ("type", "submit") : attrs
+    return $ makeElement "input" [] $ addAttrs attrs [("type", "submit")]
 
 -- | Generate a label for a field. Example:
 --
@@ -226,7 +244,7 @@
     (ref, attrs) <- getRefAttributes Nothing
     content      <- getContent
     let ref' = absoluteRef ref view
-    return $ makeElement "label" content $ ("for", ref') : attrs
+    return $ makeElement "label" content $ addAttrs attrs [("for", ref')]
 
 -- | Generate a form tag with the @method@ attribute set to @POST@ and
 -- the @enctype@ set to the right value (depending on the form).
@@ -242,10 +260,10 @@
 dfForm view = do
     (_, attrs) <- getRefAttributes Nothing
     content    <- getContent
-    return $ makeElement "form" content $
-        attrs ++ 
+    return $ makeElement "form" content $ addAttrs attrs
         [ ("method", "POST")
-        , ("enctype", T.pack (show $ viewEncType view)) ]
+        , ("enctype", T.pack (show $ viewEncType view))
+        ]
 
 errorList :: [Text] -> [(Text, Text)] -> [X.Node]
 errorList []   _     = []
