diff --git a/lib/Hikaru/Form.hs b/lib/Hikaru/Form.hs
index bf36924f39e665b06130614b33802620c9d385cb..688301372279e9c32a2b4e37321077f780e21923 100644
--- a/lib/Hikaru/Form.hs
+++ b/lib/Hikaru/Form.hs
@@ -9,8 +9,7 @@ Portability :  non-portable (ghc)
 -}
 
 module Hikaru.Form
-  ( FormData
-  , FormView(..)
+  ( FormView(..)
   , FormElement(..)
   , FormNote(..)
   , FormT
@@ -19,18 +18,19 @@ module Hikaru.Form
   , postForm
   , button
   , inputField
+  , hiddenField
+  , textField
+  , selectField
+  , multiSelectField
   )
 where
-  import BasePrelude hiding (length)
+  import BasePrelude
 
+  import Control.Monad.Reader
+  import Control.Monad.State
   import Data.Text (Text)
   import Hikaru.Action
   import Hikaru.Types
-  import Control.Monad.State
-  import Control.Monad.Reader
-
-
-  type FormData = ([(Text, Text)], [(Text, FileInfo FilePath)])
 
 
   data FormView l
@@ -39,22 +39,38 @@ where
       , formNotes      :: [FormNote l]
       }
 
-  emptyFormView :: FormView l
-  emptyFormView = FormView { formElements = []
-                           , formNotes    = []
-                           }
-
 
   data FormElement l
-    = Button
+    = HiddenField
+      { elemName       :: Text
+      , elemValue      :: Maybe Text
+      }
+    | Button
       { elemName       :: Text
       , elemLabel      :: l
+      , elemAttrs      :: [(Text, Dynamic)]
       }
     | InputField
       { elemName       :: Text
       , elemLabel      :: l
       , elemValue      :: Maybe Text
       , elemNotes      :: [FormNote l]
+      , elemAttrs      :: [(Text, Dynamic)]
+      }
+    | TextField
+      { elemName       :: Text
+      , elemLabel      :: l
+      , elemValue      :: Maybe Text
+      , elemNotes      :: [FormNote l]
+      , elemAttrs      :: [(Text, Dynamic)]
+      }
+    | SelectField
+      { elemName       :: Text
+      , elemLabel      :: l
+      , elemOptions    :: [(Text, l, Bool)]
+      , elemNotes      :: [FormNote l]
+      , elemAttrs      :: [(Text, Dynamic)]
+      , elemMulti      :: Bool
       }
 
 
@@ -99,8 +115,7 @@ where
   --
   newForm :: (MonadAction m) => Text -> FormT l m a -> m (FormView l)
   newForm name form = do
-    let env = emptyEnv { envPrefix = [name] }
-    (_, view) <- runForm env form
+    (_, view) <- runForm form emptyEnv { envPrefix = [name] }
     return view
 
 
@@ -111,12 +126,10 @@ where
   getForm name form = do
     params <- getParams
 
-    let env = emptyEnv { envPrefix    = [name]
-                       , envParams    = params
-                       , envRunChecks = True
-                       }
-
-    runForm env form
+    runForm form emptyEnv { envPrefix    = [name]
+                          , envParams    = params
+                          , envRunChecks = True
+                          }
 
 
   -- |
@@ -127,31 +140,35 @@ where
     fields <- getFields
     files  <- getFiles
 
-    let env = emptyEnv { envPrefix    = [name]
-                       , envParams    = fields
-                       , envFiles     = files
-                       , envRunChecks = True
-                       }
-
-    runForm env form
+    runForm form emptyEnv { envPrefix    = [name]
+                          , envParams    = fields
+                          , envFiles     = files
+                          , envRunChecks = True
+                          }
 
 
   -- |
   -- Unwrap the transformer stack and run the form.
   --
-  runForm :: (MonadAction m) => Env -> FormT l m a -> m (a, FormView l)
-  runForm env form = runStateT (runReaderT (unFormT form) env) emptyFormView
+  runForm :: (MonadAction m) => FormT l m a -> Env -> m (a, FormView l)
+  runForm form env = runStateT (runReaderT (unFormT form) env)
+                               FormView { formElements = []
+                                        , formNotes    = []
+                                        }
+
 
 
   button :: (Monad m) => Text -> l -> FormT l m Bool
   button name label = do
-    value    <- formParamMaybe name
     fullName <- makeName name
 
-    appendElement $ Button { elemName  = fullName
-                           , elemLabel = label
-                           }
+    appendElement
+      Button { elemName  = fullName
+             , elemLabel = label
+             , elemAttrs = []
+             }
 
+    value <- formParamMaybe name
     case value of
       Nothing -> return False
       Just () -> return True
@@ -159,19 +176,99 @@ where
 
   inputField :: (Monad m, ToParam a, FromParam a)
              => Text -> l -> Maybe a -> FormT l m (Maybe a)
-  inputField name label value = do
-    fullName <- makeName name
-    value'   <- formParamMaybe fullName
+  inputField name label orig = do
+    fullName  <- makeName name
+    textValue <- formParamMaybe fullName
+
+    let textOrig = toParam <$> orig
+     in appendElement
+          InputField { elemName  = fullName
+                     , elemLabel = label
+                     , elemValue = textValue <|> textOrig
+                     , elemAttrs = []
+                     , elemNotes = []
+                     }
+
+    formParamMaybe fullName
 
-    appendElement $ InputField { elemName  = fullName
-                               , elemLabel = label
-                               , elemValue = value' <|> fmap toParam value
-                               , elemNotes = []
-                               }
+
+  hiddenField :: (Monad m, ToParam a, FromParam a)
+              => Text -> Maybe a -> FormT l m (Maybe a)
+  hiddenField name orig = do
+    fullName  <- makeName name
+    textValue <- formParamMaybe fullName
+
+    let textOrig = toParam <$> orig
+     in appendElement
+          HiddenField { elemName  = fullName
+                      , elemValue = textValue <|> textOrig
+                      }
 
     formParamMaybe fullName
 
 
+  textField :: (Monad m, ToParam a, FromParam a)
+            => Text -> l -> Maybe a -> FormT l m (Maybe a)
+  textField name label orig = do
+    fullName  <- makeName name
+    textValue <- formParamMaybe fullName
+
+    let textOrig = toParam <$> orig
+     in appendElement
+          TextField { elemName  = fullName
+                    , elemLabel = label
+                    , elemValue = textValue <|> textOrig
+                    , elemAttrs = []
+                    , elemNotes = []
+                    }
+
+    formParamMaybe fullName
+
+
+  selectField :: (Monad m, ToParam a, FromParam a, Eq a)
+              => Text -> l -> (a -> l) -> [a] -> Maybe a -> FormT l m (Maybe a)
+  selectField name label optlabel options orig = do
+    fullName  <- makeName name
+    value     <- formParamMaybe fullName
+
+    let textOptions = [ (toParam o, optlabel o, isSel o) | o <- options ]
+        isSel o     = Just o == (value <|> orig)
+     in appendElement
+          SelectField { elemName    = fullName
+                      , elemLabel   = label
+                      , elemOptions = textOptions
+                      , elemAttrs   = []
+                      , elemNotes   = []
+                      , elemMulti   = False
+                      }
+
+    case value of
+      Just v -> if v `elem` options
+                   then return value
+                   else return Nothing
+      Nothing -> return Nothing
+
+
+  multiSelectField :: (Monad m, ToParam a, FromParam a, Eq a)
+                   => Text -> l -> (a -> l) -> [a] -> [a] -> FormT l m [a]
+  multiSelectField name label optlabel options orig = do
+    fullName     <- makeName name
+    values       <- formParamList fullName
+
+    let textOptions = [ (toParam o, optlabel o, isSel o) | o <- options ]
+        isSel o     = o `elem` (values <|> orig)
+     in appendElement
+          SelectField { elemName     = fullName
+                      , elemLabel    = label
+                      , elemOptions  = textOptions
+                      , elemAttrs    = []
+                      , elemNotes    = []
+                      , elemMulti    = True
+                      }
+
+    return $ [ v | v <- values, v `elem` options ]
+
+
   -- Form Internals ---------------------------------------------------------
 
 
@@ -187,6 +284,12 @@ where
     return $ fromParam =<< lookup name envParams
 
 
+  formParamList :: (Monad m, FromParam a) => Text -> FormT l m [a]
+  formParamList name = FormT do
+    Env{envParams} <- ask
+    return $ mapMaybe (fromParam . snd) $ filter ((name ==) . fst) $ envParams
+
+
   formFileMaybe :: (Monad m) => Text -> FormT l m (Maybe (FileInfo FilePath))
   formFileMaybe name = FormT do
     Env{envFiles} <- ask