diff --git a/Text/Formlets.hs b/Text/Formlets.hs
--- a/Text/Formlets.hs
+++ b/Text/Formlets.hs
@@ -1,78 +1,4 @@
-{-# LANGUAGE TypeOperators #-}
-module Text.Formlets ( module Text.Formlets.Form
-                     , input , password , inputF , passwordF,ensure
-                     , FailingForm, validate, runFormState, inputIntegerF
-                     , check, liftForm
-                     )
-                     where
-
-import Text.Formlets.Form
-import Control.Applicative
-import Control.Applicative.Compose
-import Control.Applicative.Error
-import Control.Applicative.State
-import Text.XHtml.Strict ((!))
-import qualified Text.XHtml.Strict as X
-
--- | Apply a predicate to a value and return Success or Failure as appropriate
-ensure :: Show a 
-       => (a -> Bool) -- ^ The predicate
-       -> String      -- ^ The error message, in case the predicate fails
-       -> a           -- ^ The value
-       -> Failing a
-ensure p msg x | p x = Success x
-               | otherwise = Failure [msg]
-
--- | Helper function for genereting input components based.
-input' :: (String -> String -> X.Html) -> Maybe String -> Form String
-input' i defaultValue = Form $ \env -> mkInput env <$> freshName
-   where mkInput :: Env -> String -> (Collector String, Xml)
-         mkInput env name = (`queryParam` name,
-                             i name (value name env))
-         value name env = maybe (maybe "" id defaultValue) id (lookup name env)
-
--- | A form whose output may fail
-type FailingForm a = (Form :+: Failing) a
-
-runFormState :: Env             -- ^ A previously filled environment (may be empty)
-             -> FailingForm a   -- ^ The form
-             -> FormState       -- ^ Initial form state
-             -> ((Collector (Failing a), Xml), FormState)
-runFormState e f s = (runState (deform (decompose f) e) s)
-
--- | Lifts a function on a Form to a function on a composed form.
-liftForm :: (Form (f a) -> Form (f a)) -> (Form :+: f) a -> (Form :+: f) a
-liftForm f = Compose . f . decompose
-
--- | Lift a form component to a failing form component
-validate :: Form a -> FailingForm a
-validate f = Compose $ pure Success <*> f
-
--- | Add additional validation to an already validated component
-check :: FailingForm a -> (a -> Failing b) -> FailingForm b
-check form f = decompose form `chk` checker f
-   where chk :: Form a -> (a -> Failing b) -> FailingForm b
-         chk form validator = Compose $ pure validator <*> form
-         checker :: (a -> Failing b) -> (Failing a -> Failing b)
-         checker f (Failure x) = Failure x
-         checker f (Success x) = f x
-
--- | Component: an input field with an optional value
-input :: Maybe String -> Form String
-input = input' (\n v -> X.textfield n ! [X.value v])
-
--- | Component: a password field with an optional value
-password :: Maybe String -> Form String
-password = input' (\n v -> X.password n ! [X.value v])
-
--- | A trivially validated input component
-inputF :: Maybe String -> FailingForm String
-inputF = validate . input
-
--- | A trivially validated password component
-passwordF :: Maybe String -> FailingForm String
-passwordF = validate . password
+module Text.Formlets (module W, module B) where
 
--- | A validated integer component
-inputIntegerF :: Maybe Integer -> FailingForm Integer
-inputIntegerF x = validate (input $ fmap show x) `check` asInteger
+import Text.Formlets.Widgets as W
+import Text.Formlets.Base as B
diff --git a/Text/Formlets/Base.hs b/Text/Formlets/Base.hs
new file mode 100644
--- /dev/null
+++ b/Text/Formlets/Base.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE TypeOperators #-}
+module Text.Formlets.Base (
+                     input', fmapFst
+                     , check, ensure, ensures
+                     , runFormState 
+                     , xml, plug
+                     , Env , Form , Plus (..)
+                     )
+                     where
+
+import Control.Applicative
+import Control.Applicative.Error
+import Control.Applicative.State
+import Data.Maybe (isJust)
+
+class Plus a where
+  zero :: a
+  plus :: a -> a -> a
+
+-- | Apply a predicate to a value and return Success or Failure as appropriate
+ensure :: Show a 
+       => (a -> Bool) -- ^ The predicate
+       -> String      -- ^ The error message, in case the predicate fails
+       -> a           -- ^ The value
+       -> Failing a
+ensure p msg x | p x = Success x
+               | otherwise = Failure [msg]
+
+-- | Apply multiple predicates to a value, return Success or all the Failure messages
+ensures :: Show a
+        => [(a -> Bool, String)] -- ^ List of predicate functions and error messages, in case the predicate fails
+        -> a                     -- ^ The value
+        -> Failing a
+ensures ps x | null errors = Success x
+             | otherwise   = Failure errors
+    where errors = [ err | (p, err) <- ps, not $ p x ]
+
+-- | Helper function for genereting input components based forms.
+input' :: (String -> String -> xml) -> Maybe String -> Form xml String
+input' i defaultValue = Form $ \env -> mkInput env <$> freshName
+   where mkInput env name = (Success . (`queryParam` (name)),
+                             i name (value name env))
+         value name env = maybe (maybe "" id defaultValue) id (lookup name env)
+
+-- | Runs the form state
+runFormState :: Env             -- ^ A previously filled environment (may be empty)
+             -> Form xml a      -- ^ The form
+             -> (Collector (Failing a), xml)
+runFormState e (Form f) = evalState (f e) 0
+
+-- | Add additional validation to an already validated component
+check :: Form xml a -> (a -> Failing b) -> Form xml b
+check (Form frm) f = Form $ \e -> checker (frm e)
+ where checker = fmap $ fmapFst (f' .)
+       f' (Failure x) = Failure x
+       f' (Success x) = f x
+
+--- Form stuff
+type Env = [(String, String)]
+type FormState = Names
+type Names = Integer
+type Name = String
+
+queryParam :: Env -> Name -> String
+queryParam env name = case (name `lookup` env) of
+                           Nothing -> error $ "Couldn't find " ++ name
+                           Just x  -> x
+
+
+newtype Form xml a = Form { deform :: Env -> State FormState (Collector (Failing a), xml) }
+
+instance Plus xml => Functor (Form xml) where
+  fmap f (Form a) = Form $ \env -> (fmap . fmapFst . fmap . fmap) f (a env)
+
+fmapFst f (a, b) = (f a, b)
+
+type Collector a = Env -> a
+
+instance Plus xml => Applicative (Form xml) where
+   pure = pureF
+   (<*>) = applyF
+
+pureF :: Plus xml => a -> Form xml a
+pureF v = Form $ \env -> pure (const (Success v), zero) -- K
+
+applyF :: Plus xml => Form xml (a -> b) -> Form xml a -> Form xml b
+(Form f) `applyF` (Form v) = Form $ \env -> pure combine <*> f env <*> v env
+  where combine (v1, xml1) (v2, xml2) = (\e -> v1 e <*> v2 e, xml1 `plus` xml2)
+
+
+-- | Component: just some xml
+xml :: xml -> Form xml ()
+xml x = Form $ \env -> pure (const $ Success (), x)
+
+-- | Transform the XML component
+plug :: Plus xml => (xml -> xml) -> Form xml a -> Form xml a
+f `plug` (Form m) = Form $ \env -> pure plugin <*> m env
+   where plugin (c, x) = (c, f x)
+
+-----------------------------------------------
+-- Private methods
+-----------------------------------------------
+
+freshName :: State FormState String
+freshName = do n <- currentName
+               modify (+1)
+               return n
+
+currentName :: State FormState String
+currentName = gets $ (++) "input" . show
diff --git a/Text/Formlets/Form.hs b/Text/Formlets/Form.hs
deleted file mode 100644
--- a/Text/Formlets/Form.hs
+++ /dev/null
@@ -1,59 +0,0 @@
-module Text.Formlets.Form where
-
-import Control.Applicative
-import Control.Applicative.State
-import Text.XHtml.Strict ((+++))
-import Text.XHtml.Strict as X
-
-type Env = [(String, String)]
-type FormState = Names
-type Names = Integer
-type Name = String
-type Xml = X.Html
-
-queryParam :: Env -> Name -> String
-queryParam env name = case (name `lookup` env) of
-                           Nothing -> error $ "Couldn't find " ++ name
-                           Just x  -> x
-
-newtype Form a = Form { deform :: Env -> State FormState (Collector a, X.Html) }
-
-instance Functor Form where
-  fmap f (Form a) = Form $ \env -> (fmap . fmapFst . fmap) f (a env)
-   where fmapFst f (a, b) = (f a, b)
-
-type Collector a = Env -> a
-
-instance Applicative Form where
-   pure = pureF
-   (<*>) = applyF
-
-pureF :: a -> Form a
-pureF v = Form $ \env -> pure (const v, X.noHtml) -- K
-
-applyF :: Form (a -> b) -> Form a -> Form b
-(Form f) `applyF` (Form v) = Form $ \env -> pure combine <*> f env <*> v env
-   where combine (f, x) (v, y) = (\e -> f e (v e), x +++ y)
-
--- TODO ORYO
-freshName :: State FormState String
-freshName = do n <- get
-               put $ n + 1
-               return $ "input_" ++ show n
-
-currentName :: State FormState String
-currentName = gets $ (++) "input_" . show
-
-{- component: just some xml -}
-xml :: X.Html -> Form ()
-xml x = Form $ \env -> pure (const (), x)
-
-{- component: just some text -}
-text :: String -> Form ()
-text s = Form $ \env -> pure (const (), toHtml s)
-
-{- transform the XML component -}
-plug :: (Xml -> Xml) -> Form a -> Form a
-f `plug` (Form m) = Form $ \env -> pure plugin <*> m env
-   where plugin :: (a, Xml) -> (a, Xml)
-         plugin (c, x) = (c, f x)
diff --git a/Text/Formlets/Widgets.hs b/Text/Formlets/Widgets.hs
new file mode 100644
--- /dev/null
+++ b/Text/Formlets/Widgets.hs
@@ -0,0 +1,48 @@
+module Text.Formlets.Widgets where
+
+import Text.Formlets.Base
+import qualified Text.XHtml.Strict as X
+import Text.XHtml.Strict ((!), (+++))
+import Control.Applicative.Error
+
+type XHtmlForm a = Form X.Html a
+
+instance Plus X.Html where
+  zero = X.noHtml
+  plus = (+++)
+
+-- | An input field with an optional value
+input :: Maybe String -> XHtmlForm String
+input = input' (\n v -> X.textfield n ! [X.value v])
+
+textarea :: Maybe String -> XHtmlForm String
+textarea = input' (\n v -> X.textarea (X.toHtml v) ! [X.name n])
+
+-- | A password field with an optional value
+password :: Maybe String -> XHtmlForm String
+password = input' (\n v -> X.password n ! [X.value v])
+
+-- | A hidden input field
+hidden  :: Maybe String -> XHtmlForm String
+hidden  =  input' X.hidden
+
+-- | A validated integer component
+inputIntegerF :: Maybe Integer -> XHtmlForm Integer
+inputIntegerF x = input (fmap show x) `check` asInteger 
+
+-- | A radio choice
+radio :: [(String, String)] -> Maybe String -> XHtmlForm String
+radio choices = input' mkRadios -- todo: validate that the result was in the choices
+ where radio n v i = X.input ! [X.thetype "radio", X.name n, X.identifier i, X.theclass "radio", X.value v]
+       mkRadios name selected = X.concatHtml $ map (mkRadio name selected) (zip choices [1..])
+       mkRadio  name selected ((value, label), idx) = (radio name value ident) ! attrs 
+                                                   +++ X.label (X.toHtml label) ! [X.thefor ident, X.theclass "radio"]
+        where attrs | selected == value = [X.checked]
+                    | otherwise = []
+              ident = name ++ "_" ++ show idx
+
+enumRadio :: (Enum a) => [(a, String)] -> Maybe a -> XHtmlForm a
+enumRadio values defaultValue = radio (map toS values) (fmap (show . fromEnum) defaultValue) 
+                                `check` convert `check` tryToEnum
+ where toS = fmapFst (show . fromEnum)
+       convert v = maybeRead' v "Conversion error" 
diff --git a/formlets.cabal b/formlets.cabal
--- a/formlets.cabal
+++ b/formlets.cabal
@@ -1,5 +1,5 @@
 Name:            formlets
-Version:         0.1
+Version:         0.2
 Synopsis:        Formlets implemented in Haskell
 Description:     A modular way to build forms based on applicative functors, as
                  described in:
@@ -14,7 +14,8 @@
 Copyright:       (c) Jeremy Yallop / Tupil
 Author:          Jeremy Yallop / Chris Eidhof
 Maintainer:      Chris Eidhof <ce+hackage@tupil.com>
-Exposed-Modules:   Text.Formlets.Form
-                 , Text.Formlets
+Exposed-Modules:   Text.Formlets
+                 , Text.Formlets.Base
+                 , Text.Formlets.Widgets
 Build-Type:      Simple
 Build-Depends:   base, haskell98, mtl, xhtml, applicative-extras
