diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # Revision history for front
 
+## 0.0.0.3 -- 2020-02-04
+
+* `Todo` example reworked to be complaint with http://todomvc.com.
+* `onBlur`, `onDoubleClick`, `onEnter` event handlers added.
+
 ## 0.0.0.2 -- 2019-04-10
 
 * Integration with Servant established.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -72,6 +72,15 @@
   -o bundle.js fay/Client.hs
 ```
 
+or
+
+```
+~/.local/bin/fay \
+  --package fay-dom,fay-websockets \
+  --include shared,fay \
+  -o bundle.js fay/Client.hs
+```
+
 Please do not hesitate to open Issue to discuss your questions or use cases.
 
 ## Acknowledgement
diff --git a/examples/todo/ServantTodo.hs b/examples/todo/ServantTodo.hs
--- a/examples/todo/ServantTodo.hs
+++ b/examples/todo/ServantTodo.hs
@@ -45,7 +45,7 @@
 import           Text.Blaze.Front.Renderer               (renderNewMarkup)
 import           Text.Blaze.Html5                        (Html)
 
-import           Control.Concurrent.STM.Lifted           as STM
+import           Control.Concurrent.STM                  as STM
 import qualified Data.ByteString.Base64                  as Base64
 import qualified Data.ByteString.Char8                   as BSC8
 import qualified Data.ByteString.Lazy                    as BL
@@ -55,6 +55,7 @@
 import qualified Text.Blaze.Front.Html5.Attributes       as A
 
 import           Bridge
+import           Shared
 import           Todo
 import           Web.Front.Broadcast
 
@@ -102,6 +103,7 @@
           H.head $ do
             H.title "TODO"
             H.script ! A.src "/static/bundle.js" $ ""
+            H.link ! A.rel "stylesheet" ! A.href "/static/todo.css"
           H.body $ do
             H.div ! A.id "root" $ renderModel state)
 
@@ -140,7 +142,7 @@
     ex :: AuthCookieExceptionHandler IO
     ex _e = pure Nothing
 
-setSession :: MonadIO m => Config -> Int -> m ()
+setSession :: Config -> Int -> IO ()
 setSession Config{..} clientId = atomically $ do
   ids <- readTVar clients
   unless (clientId `elem` ids) $ modifyTVar' clients (clientId :)
@@ -156,7 +158,7 @@
         , fkspPath = "./test-key-set"
         }
   cfg <- Config
-    <$> STM.newTVarIO initModel
+    <$> STM.newTVarIO newModel
     <*> atomically newBroadcastTChan
     <*> pure "./static"
     <*> newTVarIO []
diff --git a/examples/todo/Shared.hs b/examples/todo/Shared.hs
new file mode 100644
--- /dev/null
+++ b/examples/todo/Shared.hs
@@ -0,0 +1,90 @@
+{-# LANGUAGE CPP                #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveFunctor      #-}
+{-# LANGUAGE NoImplicitPrelude  #-}
+{-# LANGUAGE OverloadedStrings  #-}
+
+module Shared where
+
+import           Prelude
+
+import           Data.Data
+import           Data.Text (Text)
+#ifdef FAY
+import           Client
+#endif
+import           Bridge
+
+type EntryId = Int
+
+data Visibility = All | Active | Completed
+#ifdef FAY
+  deriving (Typeable, Data)
+#else
+  deriving (Show, Typeable, Data, Eq)
+#endif
+
+data Msg = Msg1 MsgCommon | Msg2 MsgEntry
+#ifdef FAY
+  deriving (Typeable, Data)
+#else
+  deriving (Show, Typeable, Data)
+#endif
+
+data MsgCommon
+  = Add
+  | UpdateField Text
+  | CheckAll Bool
+  | DeleteComplete
+  | ChangeVisibility Visibility
+#ifdef FAY
+  deriving (Typeable, Data)
+#else
+  deriving (Show, Typeable, Data)
+#endif
+
+
+data MsgEntry
+  = Editing EntryId Bool
+  | UpdateEntry EntryId Text
+  | Check EntryId Bool
+  | Delete EntryId
+#ifdef FAY
+  deriving (Typeable, Data)
+#else
+  deriving (Show, Typeable, Data)
+#endif
+
+
+#ifdef FAY
+-- | Boilerplate that teaches client how to assign value into the message.
+update :: Msg -> Text -> Msg
+-- FIXME: update (Update ix _oldval) newval = Update ix newval
+update (Msg1 msgCommon) x = Msg1 (updateMsgCommon msgCommon x)
+update (Msg2 msgEntry)  x = Msg2 (updateMsgEntry msgEntry x)
+
+updateMsgCommon :: MsgCommon -> Text -> MsgCommon
+updateMsgCommon Add _ = Add
+updateMsgCommon (UpdateField _) val2     = UpdateField val2
+updateMsgCommon (CheckAll _) val3 = case val3 of
+  "checked" -> CheckAll True
+  _         -> CheckAll False
+updateMsgCommon DeleteComplete _ = DeleteComplete
+updateMsgCommon (ChangeVisibility old) val4 = case val4 of
+  "All"       -> ChangeVisibility All
+  "Active"    -> ChangeVisibility Active
+  "Completed" -> ChangeVisibility Completed
+  _           -> ChangeVisibility old
+
+updateMsgEntry :: MsgEntry -> Text -> MsgEntry
+updateMsgEntry (Editing eid _old) val1 = case val1 of
+  "" -> Editing eid False
+  _  -> Editing eid True
+updateMsgEntry (UpdateEntry eid _) val2 = UpdateEntry eid val2
+updateMsgEntry (Check eid _) val3 = case val3 of
+  "" -> Check eid False
+  _  -> Check eid True
+updateMsgEntry (Delete eid) _ = Delete eid
+
+main = runWith update
+#endif
diff --git a/examples/todo/Todo.hs b/examples/todo/Todo.hs
--- a/examples/todo/Todo.hs
+++ b/examples/todo/Todo.hs
@@ -7,10 +7,9 @@
 module Todo where
 
 import           Bridge
-import           Control.Concurrent.STM.Lifted     as STM
+import           Control.Concurrent.STM            as STM
 import           Control.Monad                     (forM_, void)
 import           Data.Char                         (isDigit)
-import           Data.Data
 import qualified Data.List                         as L
 import           Data.Text                         (Text)
 import qualified Data.Text                         as T
@@ -25,90 +24,266 @@
 import           Web.Front
 import           Web.Front.Broadcast
 
+
+import           Shared
+
 -- * Model
 
 data Model = Model
-  { entries :: [Entry]
-  , nextId  :: Int
+  { entries    :: [Entry]
+  , field      :: Text
+  , nextId     :: EntryId
+  , visibility :: Visibility
   }
 
+newModel :: Model
+newModel = Model
+  { entries = []
+  , field = ""
+  , nextId = 0
+  , visibility = All
+  }
+
 data Entry = Entry
   { description :: Text
-  , eid         :: Int
+  , completed   :: Bool
+  , editing     :: Bool
+  , eid         :: EntryId
   }
 
-data Msg = Add
-  | Complete Int
-  | Update Int RecordValue
-  deriving (Show, Typeable, Data)
-
-initModel :: Model
-initModel = Model { entries = [], nextId = 0 }
+newEntry :: Text -> EntryId -> Entry
+newEntry desc _eid = Entry
+  { description = desc
+  , completed = False
+  , editing = False
+  , eid = _eid
+  }
 
 -- * Event Handling
 
 instance CommandHandler TVar Model Msg where
-  onCommand cmd stateTVar client = do
-    m@Model{..} <- STM.readTVarIO stateTVar
+  onCommand cmd stateTVar' client = do
+    putStrLn (show cmd)
+    m@Model{..} <- STM.readTVarIO stateTVar'
     case readFromFay' cmd of
       Left err -> do
         putStrLn $ "Error: " <> show err
         return (ExecuteClient client emptyTask ExecuteAll)
       Right (Send (Action _ _ acmd)) -> do
         case acmd of
-          Add -> do
-            let newTodo = Entry "TODO: " nextId
-                newState = Model (newTodo : entries) (succ nextId)
-                task = createTask "root" renderModel newState
-            atomically $ void $ swapTVar stateTVar newState
-            return (ExecuteClient client task ExecuteAll)
-          Complete _eid -> do
-            let newState = Model (filter ((/=_eid) . eid) entries) nextId
-                task = createTask "root" renderModel newState
-            atomically $ void $ swapTVar stateTVar newState
-            return (ExecuteClient client task ExecuteAll)
-          Update _eid val -> do
-            let newState = Model ((upd _eid val) <$> entries) nextId
-                upd _id val' e@Entry{..} =
-                  if eid == _id then e { description = val', eid = _id } else e
-                task = createTask "root" renderModel newState
-            atomically $ void $ swapTVar stateTVar newState
-            return (ExecuteClient client task ExecuteExcept)
+          Msg1 msgCommon -> onMsgCommon m msgCommon stateTVar' client
+          Msg2 msgEntry  -> onMsgEntry m msgEntry stateTVar' client
       Right AskEvents -> do
         let task = createTask "root" renderModel m
         return $ ExecuteClient client task ExecuteAll
       Right PingPong -> return (ExecuteClient client emptyTask ExecuteAll)
 
+onMsgCommon
+  :: Model -> MsgCommon -> TVar Model -> ClientId -> IO (Out (Action Msg))
+onMsgCommon m@Model{..} msg tvar client = case msg of
+  Add -> do
+    let update t =
+          t { entries = newEntry field nextId : entries, nextId = succ nextId, field = "" }
+    go (\t _ -> update t) ()
+  UpdateField val -> go2 (\t v -> t { field = v }) val
+  CheckAll isChecked -> do
+    let update v e = e { completed = v }
+    go (\t v -> t { entries = update v <$> entries }) isChecked
+  DeleteComplete ->
+    go (\t _ -> t { entries = filter (not . completed) entries } ) ()
+  ChangeVisibility v -> go (\t x -> t { visibility = x }) v
+  where
+    go = pushAll m tvar client
+    go2 = pushExcept m tvar client
+
+onMsgEntry
+  :: Model -> MsgEntry -> TVar Model -> ClientId -> IO (Out (Action Msg))
+onMsgEntry m@Model{..} msg tvar client = case msg of
+  Editing entryId val -> do
+    let update v t = t { editing = v }
+    go (\t v -> updateModel t v entryId update) val
+  Check entryId val -> do
+    let update v t = t { completed = v }
+    go (\t v -> updateModel t v entryId update) val
+  UpdateEntry entryId val -> do
+    let update desc t = t { description = desc }
+    go2 (\t v -> updateModel t v entryId update) val
+  Delete entryId -> do
+    let update e v = e { entries = filter ((/= v) . eid) entries }
+    go update entryId
+  where
+    go = pushAll m tvar client
+    go2 = pushExcept m tvar client
+    updateModel :: Model -> val -> EntryId -> (val -> Entry -> Entry) -> Model
+    updateModel t v i f = t { entries = filterMap entries ((== i) . eid) (f v) }
+
+-- * Helpers
+
+pushExcept
+  :: state
+  -> TVar Model
+  -> ClientId
+  -> (state -> val -> Model)
+  -> val
+  -> IO (Out (Action Msg))
+pushExcept model tvar client f val =
+  push model tvar client f val ExecuteExcept
+
+pushAll
+  :: state
+  -> TVar Model
+  -> ClientId
+  -> (state -> val -> Model)
+  -> val
+  -> IO (Out (Action Msg))
+pushAll model tvar client f val =
+  push model tvar client f val ExecuteAll
+
+push
+  :: state
+  -> TVar Model
+  -> ClientId
+  -> (state -> val -> Model)
+  -> val
+  -> ExecuteStrategy
+  -> IO (Out (Action Msg))
+push model tvar client f val execStrategy = do
+  let newState = f model val
+      task = createTask "root" renderModel newState
+  atomically $ void $ swapTVar tvar newState
+  pure (ExecuteClient client task execStrategy)
+
 -- * Rendering
 
 renderModel :: Model -> H.Markup (Action Msg)
 renderModel Model{..} = do
-  H.h1 $ "TODO MVC: Servant"
-  H.br
-  forM_ entries $ \entry -> renderEntry entry
-  let btnId = "todo-add"
-  H.button
-    ! A.id "todo-add"
-    ! A.type_ "submit"
-    ! E.onClick (Action btnId ObjectAction Add)
-    $ "Add"
+  H.div ! A.class_ "todomvc-wrapper" $ do
+    H.section ! A.class_ "todoapp" $ do
+      H.header ! A.class_ "header" $ do
+        H.h1 $ "todos"
+        H.input
+          ! A.class_ "new-todo"
+          ! A.id "new-todo"
+          ! A.placeholder "What needs to be done?"
+          ! A.autofocus ""
+          ! A.value (toValue field)
+          ! E.onEnter (Action "new-todo" EnterAction (Msg1 Add))
+          ! E.onKeyPress (Action "new-todo" RecordAction (Msg1 $ UpdateField field))
+          ! A.name "newTodo"
 
+      renderEntries visibility entries
+      renderControls visibility entries
+
+    H.footer ! A.class_ "info" $ do
+      H.p $ "Double-click to edit todo"
+      H.p $ do
+        H.span $ "Written by "
+        H.a ! A.href "https://github.com/swamp-agr" $ "Andrey Prokopenko"
+      H.p $ do
+        H.span "Part of "
+        H.a ! A.href "https://todomvc.com" $ "TODO MVC"
+
+renderEntries :: Visibility -> [Entry] -> H.Markup (Action Msg)
+renderEntries visibility entries = do
+  H.section
+    ! A.class_ "main" ! A.style ("visibility: " <> cssVisibility entries <> ";")
+  $ do H.input
+         ! A.class_ "toggle-all"
+         ! A.type_ "checkbox"
+         ! A.name "toggle"
+         ! checkIfTrue allCompleted
+       H.label ! A.for "toggle-all" $ "Mark all as complete"
+       H.ul ! A.class_ "todo-list" $ do
+         forM_ (filter isVisible entries) $ \entry -> renderEntry entry
+  where
+    cssVisibility [] = "hidden"
+    cssVisibility _  = "visible"
+
+    allCompleted = all (==True) $ completed <$> entries
+    isVisible Entry{..} =
+      case visibility of
+        Completed -> completed
+        Active    -> not completed
+        _         -> True
+
 renderEntry :: Entry -> H.Markup (Action Msg)
 renderEntry Entry{..} = do
-  let elemId = "todo-" <> (T.pack $ show eid)
-      removeId = "remove-" <> (T.pack $ show eid)
-  H.input
-    ! A.id (toValue elemId)
-    ! A.type_ "text"
-    ! A.value (toValue description)
-    ! E.onKeyUp (Action elemId RecordAction (Update eid ""))
-  H.button
-    ! A.id (toValue removeId)
-    ! A.type_ "submit"
-    ! E.onClick (Action removeId ObjectAction (Complete eid))
-    $ "Done"
+  let elemId = "todo-" <> tid
+      labelId = "label-" <> tid
+      checkId = "check-" <> tid
+      removeId = "remove-" <> tid
+      tid = T.pack $ show eid
+  H.li
+    ! A.class_ (toValue $ T.intercalate " " $
+                 [ "completed" | completed ] <> [ "editing" | editing ])
+    $ do
+        H.div ! A.class_ "view" $ do
+          H.input
+            ! A.class_ "toggle"
+            ! A.id (toValue checkId)
+            ! A.type_ "checkbox"
+            ! checkIfTrue completed
+            ! A.value ""
+            ! E.onClick
+                (Action checkId ObjectAction (Msg2 $ Check eid (not completed)))
+          H.label
+            ! A.id (toValue labelId)
+            ! E.onDoubleClick
+                (Action labelId ObjectAction (Msg2 $ Editing eid True))
+            $ H.toHtml description
+          H.button
+            ! A.id (toValue removeId)
+            ! A.class_ "destroy"
+            ! A.type_ "submit"
+            ! E.onClick
+                (Action removeId ObjectAction (Msg2 $ Delete eid))
+            $ ""
+        H.input
+          ! A.class_ "edit"
+          ! A.value (toValue description)
+          ! A.name "title"
+          ! A.id (toValue elemId)
+          ! E.onValueChange
+              (Action elemId RecordAction (Msg2 $ UpdateEntry eid description))
+          ! E.onBlur
+              (Action elemId ObjectAction (Msg2 $ Editing eid False))
+          ! E.onEnter
+              (Action elemId EnterAction (Msg2 $ Editing eid False))
+
   H.br
 
+renderControls :: Visibility -> [Entry] -> H.Markup (Action Msg)
+renderControls visibility entries = do
+  H.footer ! A.class_ "footer" ! hideIfTrue (null entries) $ do
+    H.span ! A.class_ "todo-count" $ do
+      H.strong (H.toHtml $ show entriesLeft)
+      H.span $ H.toHtml
+        (" item" <> (if entriesLeft == 1 then "" else "s") <> " left" :: String)
+
+    H.ul ! A.class_ "filters" $ do
+      swapVisibility "#/" All visibility
+      swapVisibility "#/active" Active visibility
+      swapVisibility "#/completed" Completed visibility
+
+    H.button ! A.class_ "clear-completed"
+      ! A.id "clear-completed"
+      ! hideIfTrue (entriesCompleted == 0)
+      ! E.onClick (Action "clear-completed" ObjectAction  (Msg1 DeleteComplete))
+      $ H.toHtml ("Clear completed (" <> show entriesCompleted <> ")")
+
+  where
+    entriesLeft = length entries - entriesCompleted
+    entriesCompleted = length . filter completed $ entries
+    swapVisibility :: Text -> Visibility -> Visibility -> H.Markup (Action Msg)
+    swapVisibility uri visibility' actualVisibility = H.li $ do
+      let uid = T.pack $ show visibility'
+      H.a ! A.href (toValue uri)
+          ! A.id (toValue uid)
+          ! A.class_ (toValue $ T.concat [ "selected" | visibility' == actualVisibility ])
+          ! E.onClick
+              (Action uid ObjectAction (Msg1 $ ChangeVisibility visibility'))
+          $ H.toHtml uid
+
 -- * Helpers
 
 parseInt :: Text -> Int
@@ -123,3 +298,16 @@
 safeFromJust :: forall t. t -> Maybe t -> t
 safeFromJust defv Nothing = defv
 safeFromJust _ (Just a)   = a
+
+filterMap :: [a] -> (a -> Bool) -> (a -> a) -> [a]
+filterMap xs f g = (\x -> if f x then g x else x) <$> xs
+
+hideIfTrue :: Bool -> (H.Attribute (Action Msg))
+hideIfTrue predicate = if predicate
+  then A.hidden ""
+  else A.attribute "" "" ""
+
+checkIfTrue :: Bool -> (H.Attribute (Action Msg))
+checkIfTrue predicate = if predicate
+  then A.checked "checked"
+  else A.attribute "" "" ""
diff --git a/examples/todo/YesodTodo.hs b/examples/todo/YesodTodo.hs
--- a/examples/todo/YesodTodo.hs
+++ b/examples/todo/YesodTodo.hs
@@ -12,18 +12,19 @@
 module YesodTodo where
 
 import           Conduit
-import           Control.Concurrent.STM.Lifted as STM
+import           Control.Concurrent.STM    as STM
 import           Control.Monad.Reader
-import           Prelude                       hiding (interact)
-import           System.Random                 (randomRIO)
-import           Text.Blaze.Front.Renderer     (renderNewMarkup)
+import           Prelude                   hiding (interact)
+import           System.Random             (randomRIO)
+import           Text.Blaze.Front.Renderer (renderNewMarkup)
 import           Yesod.Core
 import           Yesod.Static
 import           Yesod.WebSockets
 
-import qualified Data.Text                     as T
+import qualified Data.Text                 as T
 
 import           Bridge
+import           Shared
 import           Todo
 import           Web.Front.Broadcast
 
@@ -58,7 +59,7 @@
 getHomeR :: Handler Html
 getHomeR = do
   modelTVar <- appModel <$> getYesod
-  model <- STM.readTVarIO modelTVar
+  model <- liftIO $ STM.readTVarIO modelTVar
   webSockets $ webApp modelTVar
   defaultLayout $ do
     setTitle "TODO"
@@ -71,7 +72,7 @@
   conn <- ask
   cid <- clientSession
   writeChan' <- appChannel <$> getYesod
-  readChan' <- atomically $ do
+  readChan' <- liftIO $ atomically $ do
     dupTChan writeChan'
   liftIO $ interact conn writeChan' readChan' tvar cid
 
@@ -93,7 +94,7 @@
 main :: IO ()
 main = do
   (App
-    <$> STM.newTVarIO initModel
+    <$> STM.newTVarIO newModel
     <*> atomically newBroadcastTChan
     <*> staticDevel "./static")
   >>= warp 3000
diff --git a/front.cabal b/front.cabal
--- a/front.cabal
+++ b/front.cabal
@@ -2,7 +2,7 @@
 --  see http://haskell.org/cabal/users-guide/
 
 name:                front
-version:             0.0.0.2
+version:             0.0.0.3
 synopsis:            A reactive frontend web framework
 description:         A reactive frontend web framework. See haskell-front.org for more details.
 homepage:            haskell-front.org
@@ -14,7 +14,7 @@
 category:            Web
 build-type:          Simple
 extra-source-files:  ChangeLog.md, README.md
-cabal-version:       >=1.10
+cabal-version:       >=2.0
 
 source-repository head
   type: git
@@ -35,11 +35,10 @@
                      , Bridge
   -- other-modules:
   other-extensions:    FlexibleInstances, TypeSynonymInstances, OverloadedStrings, GeneralizedNewtypeDeriving, Rank2Types, ExistentialQuantification, DeriveDataTypeable, MultiParamTypeClasses, DeriveFunctor, FunctionalDependencies, ScopedTypeVariables
-  build-depends:       base <5
+  build-depends:       base >=4.0 && <5
                      , aeson
                      , async
                      , stm
-                     , stm-lifted
                      , conduit
                      , text >=1.2 && <1.3
                      , mtl
@@ -66,7 +65,7 @@
     buildable: False
   else
     hs-source-dirs:      examples/todo
-    build-depends: base
+    build-depends: base >=4.0 && <5
                  , aeson
                  , conduit
                  , async
@@ -75,7 +74,6 @@
                  , unordered-containers
                  , stm
                  , random
-                 , stm-lifted     >=0.1.1.0
                  , fay
                  , base-compat    >= 0.9.1
                  , base64-bytestring
@@ -101,7 +99,7 @@
                  , wai            >= 3.0 
                  , warp           >= 3.0
                  , front
-    other-modules: Todo
+    other-modules: Todo, Shared
     ghc-options:    -main-is ServantTodo -Wall
     default-language: Haskell2010
 
@@ -111,7 +109,7 @@
     buildable: False
   else
     hs-source-dirs:      examples/todo
-    build-depends: base
+    build-depends: base >=4.0 && <5
                  , front
                  , text
                  , mtl
@@ -122,9 +120,9 @@
                  , yesod-websockets
                  , conduit
                  , aeson
-                 , stm-lifted      >=0.1.1.0
+                 , stm
                  , bytestring
   
-    other-modules: Todo
+    other-modules: Todo, Shared
     ghc-options:    -main-is YesodTodo -Wall
     default-language: Haskell2010
diff --git a/shared/Bridge.hs b/shared/Bridge.hs
--- a/shared/Bridge.hs
+++ b/shared/Bridge.hs
@@ -17,6 +17,7 @@
     = OnKeyDown   !a
     | OnKeyUp     !a
     | OnKeyPress  !a
+    | OnEnter     !a
     | OnFocus !a
     | OnBlur  !a
     | OnValueChange    !a
@@ -108,7 +109,7 @@
 updateAction :: Action a -> a -> Action a
 updateAction (Action e a _) c = Action e a c-}
 
-data ActionType = RecordAction | ObjectAction
+data ActionType = RecordAction | ObjectAction | EnterAction
 #ifdef FAY
   deriving (Typeable, Data)
 #else
diff --git a/src/Text/Blaze/Front/Event.hs b/src/Text/Blaze/Front/Event.hs
--- a/src/Text/Blaze/Front/Event.hs
+++ b/src/Text/Blaze/Front/Event.hs
@@ -6,42 +6,44 @@
 
       -- ** Keyboard events
     , onKeyDown
-    , onKeyUp  
+    , onKeyUp
     , onKeyPress
+    , onEnter
 
       -- ** Focus events
-    , onFocus   
-    , onBlur    
+    , onFocus
+    , onBlur
 
       -- ** Form events
     , onValueChange
     , onCheckedChange
     , onSelectedChange
-    , onSubmit        
+    , onSubmit
 
       -- ** Mouse events
-    , onClick         
-    , onDoubleClick   
-    , onMouseDown     
-    , onMouseUp       
-    , onMouseMove     
-    , onMouseEnter    
-    , onMouseLeave    
-    , onMouseOver     
-    , onMouseOut      
+    , onClick
+    , onDoubleClick
+    , onMouseDown
+    , onMouseUp
+    , onMouseMove
+    , onMouseEnter
+    , onMouseLeave
+    , onMouseOver
+    , onMouseOut
 
       -- ** UI Events
-    , onScroll        
+    , onScroll
 
       -- ** Wheel Events
-    , onWheel         
+    , onWheel
 
     ) where
 
-import Text.Blaze.Front.Internal       (MarkupM(..), Attribute(..), Markup)
-import Prelude
+import           Prelude
+import           Text.Blaze.Front.Internal (Attribute (..), Markup,
+                                            MarkupM (..))
 
-import Bridge
+import           Bridge
 
 -- | Modify all event handlers attached to a 'Markup' tree so that the given
 -- function is applied to the return values of their callbacks.
@@ -53,15 +55,19 @@
 
 -- | The user has pressed a physical key while the target element was focused.
 onKeyDown :: act -> Attribute act
-onKeyDown = onEvent . OnKeyDown 
+onKeyDown = onEvent . OnKeyDown
 
 -- | The user has released a phyiscal key while the target element was focused.
 onKeyUp :: act -> Attribute act
 onKeyUp = onEvent . OnKeyUp
 
--- | The user has input some ASCII character while the target element was
+-- | The user has input some ASCII character while the target element was focused.
 onKeyPress :: act -> Attribute act
 onKeyPress = onEvent . OnKeyPress
+
+-- | The user has pressed <Enter> while the target element was focused.
+onEnter :: act -> Attribute act
+onEnter = onEvent . OnEnter
 
 -- Focus events
 -------------------------------------------------------------------------------
diff --git a/src/Text/Blaze/Front/Html5/Attributes.hs b/src/Text/Blaze/Front/Html5/Attributes.hs
--- a/src/Text/Blaze/Front/Html5/Attributes.hs
+++ b/src/Text/Blaze/Front/Html5/Attributes.hs
@@ -4,8 +4,8 @@
 -- | This module exports combinators that provide you with the
 -- ability to set attributes on HTML elements.
 --
-{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
 module Text.Blaze.Front.Html5.Attributes
     ( accept
     , accesskey
@@ -14,6 +14,7 @@
     , allowtransparency
     , alt
     , async
+    , attribute
     , autocapitalize
     , autocomplete
     , autocorrect
@@ -111,8 +112,8 @@
 -- src/Util/GenerateHtmlCombinators.hs:99
 --
 
-import Text.Blaze.Front.Internal
-  ( Attribute, AttributeValue, attribute )
+import           Text.Blaze.Front.Internal (Attribute, AttributeValue,
+                                            attribute)
 
 -- WARNING: The next block of code was automatically generated by
 -- src/Util/GenerateHtmlCombinators.hs:249
diff --git a/src/Web/Front/Broadcast.hs b/src/Web/Front/Broadcast.hs
--- a/src/Web/Front/Broadcast.hs
+++ b/src/Web/Front/Broadcast.hs
@@ -4,16 +4,16 @@
 import           Bridge
 import           Conduit
 import           Control.Concurrent.Async
-import           Control.Concurrent.STM.Lifted as STM
-import           Control.Monad                 (forever)
-import           Data.Aeson                    (Value, decode, toJSON)
+import           Control.Concurrent.STM   as STM
+import           Control.Monad            (forever)
+import           Data.Aeson               (Value, decode, toJSON)
 import           Data.Aeson.Text
-import qualified Data.ByteString.Lazy          as BL
+import qualified Data.ByteString.Lazy     as BL
 import           Data.Data
-import           Data.Text.Encoding            (encodeUtf8)
-import           Data.Text.Lazy.Builder        (toLazyText)
-import           Fay.Convert                   (showToFay)
-import           Network.WebSockets            hiding (Headers)
+import           Data.Text.Encoding       (encodeUtf8)
+import           Data.Text.Lazy.Builder   (toLazyText)
+import           Fay.Convert              (showToFay)
+import           Network.WebSockets       hiding (Headers)
 
 -- | The common way how to use websocket 'Connection' obtained from 'Handler' via 'Conduit'.
 -- 'interact' starts two concurrent processes.
