diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,31 @@
+# 1.0.0
+
+* Bindings to react-intl (http://formatjs.io/react/) for i18n support.  This is useful even if your app is
+  a single language, as it allows easy number, date, relative time, and message formatting like pluralization.
+  It also supports multiple locales and translations of messages.
+
+* The type of `callback` has extended to allow arbitrary function properties to be
+  passed to foreign classes.  The old `callback` accepted callbacks of type `Aeson.Value -> handler`
+  while the new callback allows functions of any number of arguments, as long as each argument implements
+  `FromJSRef`.  Since `Aeson.Value` implements `FromJSRef`, any existing calls to `callback` should still work.
+  This change also caused some changes to types in `React.Flux.Internal`.
+
+* Add a function `nestedProperty` to `React.Flux.PropertiesAndEvents` to create nested properties.
+
+* Support for React 0.14
+    * React 0.13 and 0.14 are both supported from the same Haskell code, the differences are handled internally.
+    * If you are using React 0.14, you will have to include `react-dom.min.js` and make sure the
+      `ReactDOM` variable is protected by closure similar to how `React` must be protected.
+    * `initializeTouchEvents` has been removed from React 0.14, so you can remove the call from your app.
+    * The SVG `image_` tag is now supported by `React.Flux.DOM`.
+    * The new media events on images and videos don't have direct Haskell equivalents, instead the handlers can be
+      created by the new `on` function in `React.Flux.PropertiesAndEvents`.
+    * The CSS transitions in `React.Flux.Addons.React` were made simpler by just passing the raw
+      properties.  There were several changes to the possible properties in React 0.14 and covering them all
+      from Haskell is not worth it when the properties can easily be created directly.
+
+* `reactRenderToString` was added to allow executing a react-flux application using node.
+
 # 0.9.4
 
 * Fix to build with latest ghcjs-base (requires at least aaa4d59117f37d1b9c60a154a9128b3bcc6301cd)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -46,23 +46,25 @@
 echo "compiler: ghcjs" > cabal.config
 cabal configure -fexample -ftest-client
 cabal build
-cd example/todo
-make
 ~~~
 
-The above builds the TODO application, compresses it with closure, and builds the test client.
+The above builds the TODO application and the test client.
 Next, install [selenium-server-standalone](http://www.seleniumhq.org/download/) (also from
 [npm](https://www.npmjs.com/package/selenium-server-standalone-jar)).  Then, build the
 [hspec-webdriver](https://hackage.haskell.org/package/hspec-webdriver) test suite using GHC (not
-GHCJS).  I use stack for this, although you can use cabal too if you like.
+GHCJS).  I use stack for this, although you can use cabal too if you like.  Also, at the moment, the prerelease
+of the react-intl library must be installed from npm.
 
 ~~~
-cd test/spec
+cd test/client
+npm install react-intl@next
+cd ../spec
 stack build
 ~~~
 
-Finally, start selenium-server-standalone and execute the test suite.  It must be started from the
-`test/spec` directory, otherwise it does not find the correct javascript files.
+Finally, start selenium-server-standalone and execute the test suite.  Make sure you also have
+closure installed, since the test suite will compress the todo app before testing it.  It must be
+started from the `test/spec` directory, otherwise it does not find the correct javascript files.
 
 ~~~
 stack exec react-flux-spec
diff --git a/example/README.md b/example/README.md
--- a/example/README.md
+++ b/example/README.md
@@ -10,7 +10,7 @@
 
 When reading the code for the example application, you should start with `TodoStore.hs`.  Next, look
 at `TodoDispatcher.hs` and `TodoViews.hs`.  Finally, you can look at `TodoComponents.hs` and
-`Main.hs`.
+`Main.hs` and `NodeMain.hs`.
 
 ### Build
 
@@ -21,7 +21,9 @@
 cabal build
 ~~~
 
-The result of this build is a file `dist/build/todo/todo.jsexe/all.js`.  There is a file
+### TODO in the browser
+
+A result of this build is a file `dist/build/todo/todo.jsexe/all.js`.  There is a file
 `example/todo/todo-dev.html` which loads this `all.js` file directly from the `dist` directory, so you
 can open `todo-dev.html` after building.
 
@@ -35,7 +37,18 @@
 ~~~
 
 This produces a file `js-build/todo.min.js` which is only 500 kibibytes which when compressed with
-gzip is 124 kibibytes.
+gzip is 124 kibibytes.  You can then open `example/todo/todo.html` which loads this minimized javascript.
+
+### TODO in node
+
+`NodeMain.hs` is a separate main module which instead of rendering the TODO example application into the DOM,
+it renders it to a string and then displays it.  To execute this, run
+
+~~~
+cd example/todo
+npm install react@0.13.3
+node run-in-node.js
+~~~
 
 ### Testing
 
diff --git a/example/todo/Makefile b/example/todo/Makefile
--- a/example/todo/Makefile
+++ b/example/todo/Makefile
@@ -3,9 +3,9 @@
 
 js-build/todo.js: ../../dist/build/todo/todo.jsexe/all.js
 	mkdir -p js-build
-	echo "(function(global,React) {" > js-build/todo.js
+	echo "(function(global,React,ReactDOM) {" > js-build/todo.js
 	cat ../../dist/build/todo/todo.jsexe/all.js >> js-build/todo.js
-	echo "})(this, window['React']);" >> js-build/todo.js
+	echo "})(window, window['React'], window['ReactDOM']);" >> js-build/todo.js
 	sed -i 's/goog.provide.*//' js-build/todo.js
 	sed -i 's/goog.require.*//' js-build/todo.js
 	# https://github.com/ghcjs/shims/issues/21
diff --git a/example/todo/NodeMain.hs b/example/todo/NodeMain.hs
new file mode 100644
--- /dev/null
+++ b/example/todo/NodeMain.hs
@@ -0,0 +1,6 @@
+import React.Flux
+import TodoViews
+import qualified Data.Text.IO as T
+
+main :: IO ()
+main = reactRenderToString True todoApp () >>= T.putStrLn
diff --git a/example/todo/run-in-node.js b/example/todo/run-in-node.js
new file mode 100644
--- /dev/null
+++ b/example/todo/run-in-node.js
@@ -0,0 +1,2 @@
+React = require("react");
+require("../../dist/build/todo-node/todo-node.jsexe/all.js");
diff --git a/example/todo/todo-dev.html b/example/todo/todo-dev.html
--- a/example/todo/todo-dev.html
+++ b/example/todo/todo-dev.html
@@ -7,7 +7,8 @@
     </head>
     <body>
         <section id="todoapp"/>
-        <script src="https://fb.me/react-0.13.3.js"></script>
+        <script src="https://fb.me/react-0.14.0-rc1.js"></script>
+        <script src=" https://fb.me/react-dom-0.14.0-rc1.js"></script>
         <script src="../../dist/build/todo/todo.jsexe/all.js"></script>
     </body>
 </html>
diff --git a/example/todo/todo.html b/example/todo/todo.html
--- a/example/todo/todo.html
+++ b/example/todo/todo.html
@@ -7,7 +7,8 @@
     </head>
     <body>
         <section id="todoapp"/>
-        <script src="https://fb.me/react-0.13.3.js"></script>
+        <script src="https://fb.me/react-0.14.0-rc1.js"></script>
+        <script src=" https://fb.me/react-dom-0.14.0-rc1.js"></script>
         <script src="js-build/todo.min.js"></script>
     </body>
 </html>
diff --git a/jsbits/callback.js b/jsbits/callback.js
new file mode 100644
--- /dev/null
+++ b/jsbits/callback.js
@@ -0,0 +1,9 @@
+function hsreact$mk_arguments_callback(f) {
+    return function() {
+        var args = new Array(arguments.length);
+        for (var i = 0; i < arguments.length; i++) {
+            args[i] = arguments[i];
+        }
+        f(args);
+    };
+}
diff --git a/jsbits/views.js b/jsbits/views.js
--- a/jsbits/views.js
+++ b/jsbits/views.js
@@ -130,3 +130,14 @@
 
     return React['createClass'](cl);
 }
+
+//React 0.14 introduced React.Children.toArray.  Also, to be able to run template haskell splices,
+//we need to defend againsg React not being defined.
+var hsreact$children_to_array = typeof React !== "object" ? null : (React['Children']['toArray'] ? React['Children']['toArray'] :
+    (function (children) {
+        var ret = [];
+        React['Children']['forEach'](children, function(x) {
+            ret.push(x);
+        });
+        return ret;
+    }));
diff --git a/react-flux.cabal b/react-flux.cabal
--- a/react-flux.cabal
+++ b/react-flux.cabal
@@ -1,5 +1,5 @@
 name:                react-flux
-version:             0.9.4
+version:             1.0.0
 synopsis:            A binding to React based on the Flux application architecture for GHCJS
 category:            Web
 homepage:            https://bitbucket.org/wuzzeb/react-flux
@@ -18,15 +18,16 @@
     example/css/bg.png,
     example/todo/Makefile,
     example/todo/*.hs,
-    example/todo/*.html
+    example/todo/*.html,
+    example/todo/run-in-node.js,
     example/routing/*.hs,
     example/routing/*.html,
-    test/client/test-client.html,
+    test/client/*.html,
+    test/client/msgs/*.json,
+    test/client/msgs/*.xml,
     test/spec/react-flux-spec.cabal,
-    test/spec/Spec.hs,
     test/spec/stack.yaml,
-    test/spec/TestClientSpec.hs,
-    test/spec/TodoSpec.hs
+    test/spec/*.hs
 
 source-repository head
     type: mercurial
@@ -44,7 +45,10 @@
   hs-source-dirs:      src
   ghc-options: -Wall
   default-language: Haskell2010
-  js-sources: jsbits/views.js jsbits/store.js jsbits/export.js
+  js-sources: jsbits/views.js
+              jsbits/store.js
+              jsbits/export.js
+              jsbits/callback.js
 
   exposed-modules: React.Flux
                    React.Flux.DOM
@@ -53,6 +57,7 @@
                    React.Flux.Internal
                    React.Flux.Addons.React
                    React.Flux.Addons.Bootstrap
+                   React.Flux.Addons.Intl
 
   other-modules: React.Flux.Views
                  React.Flux.Store
@@ -76,6 +81,10 @@
                , mtl >= 2.1
                , aeson >= 0.8
                , text >= 1.2
+               , time >= 1.5
+               , unordered-containers
+               , bytestring
+               , template-haskell >= 2.10
 
   if impl(ghcjs)
       build-depends: ghcjs-base
@@ -93,7 +102,20 @@
                 , react-flux
                 , deepseq
 
-executable test-client
+executable todo-node
+   if !flag(example)
+        Buildable: False
+   ghc-options: -Wall
+
+   default-language: Haskell2010
+   hs-source-dirs: example/todo
+   main-is: NodeMain.hs
+   build-depends: base
+                , react-flux
+                , deepseq
+                , text
+
+executable test-client-13
    if !flag(test-client)
         Buildable: False
    ghc-options: -Wall
@@ -101,9 +123,29 @@
 
    default-language: Haskell2010
    hs-source-dirs: test/client
-   main-is: TestClient.hs
+   other-modules: TestClient
+   main-is: TestClient13.hs
    build-depends: base
                 , react-flux
+                , time
+                , deepseq
+   if impl(ghcjs)
+      build-depends: ghcjs-base
+
+executable test-client-14
+   if !flag(test-client)
+        Buildable: False
+   ghc-options: -Wall
+   --cpp-options: -DGHCJS_BROWSER
+
+   default-language: Haskell2010
+   hs-source-dirs: test/client
+   other-modules: TestClient
+   main-is: TestClient14.hs
+   build-depends: base
+                , react-flux
+                , time
+                , deepseq
    if impl(ghcjs)
       build-depends: ghcjs-base
 
diff --git a/src/React/Flux.hs b/src/React/Flux.hs
--- a/src/React/Flux.hs
+++ b/src/React/Flux.hs
@@ -19,14 +19,25 @@
 -- which calls 'reactRender' and initializes any AJAX load calls to the backend. The source package
 -- contains some <https://bitbucket.org/wuzzeb/react-flux/src/tip/example/ example applications>.
 --
--- __Deployment__: Care has been taken to make sure closure with ADVANCED_OPTIMIZATIONS correctly
--- minimizes a react-flux application.  No externs are needed, instead all you need to do is provide
--- or protect the @React@ variable.  The TODO example does this as follows:
+-- __Web Deployment__: 'reactRender' is used to render your application into the DOM.
+-- Care has been taken to make sure closure with ADVANCED_OPTIMIZATIONS correctly
+-- minimizes a react-flux application.  No externs are needed, instead all you need to do is
+-- protect the @React@ variable (and @ReactDOM@ if you are using version >= 0.14).  The TODO example
+-- does this as follows:
 --
--- >(function(global, React) {
+-- >(function(global, React, ReactDOM) {
 -- >contents of all.js
--- >})(this, window['React']);
+-- >})(window, window['React'], window['ReactDOM']);
 --
+-- __Node Deployment__: 'reactRenderToString' is used to render the application to a string when
+-- running in node (not the browser).  To execute with node, you need to get @global.React@ (and
+-- @global.ReactDOM@ for 0.14) before executing all.js.  The TODO example application does this by
+-- creating a file @run-in-node.js@ with the contents
+--
+-- >React = require("react");
+-- >ReactDOM = require("react-dom");
+-- >require("../../dist/build/todo-node/todo-node.jsexe/all.js");
+--
 -- __Testing__:  I use the following approach to test my react-flux application.  First, I use unit
 -- testing to test the dispatcher and store 'transform' functions.  Since the dispatcher and the
 -- store transform are just data manipulation, existing Haskell tools like hspec, QuickCheck,
@@ -77,12 +88,14 @@
 
   -- * Main
   , reactRender
+  , reactRenderToString
 
   -- * Performance
   -- $performance
 ) where
 
 import Data.Typeable (Typeable)
+import Data.Text (Text)
 
 import React.Flux.Views
 import React.Flux.DOM
@@ -91,14 +104,15 @@
 import React.Flux.Store
 
 #ifdef __GHCJS__
-import GHCJS.Types (JSString)
+import GHCJS.Types (JSString, JSRef)
+import GHCJS.Marshal (fromJSRef)
 #endif
 
 ----------------------------------------------------------------------------------------------------
 -- reactRender has two versions
 ----------------------------------------------------------------------------------------------------
 
--- | Render your React application into the DOM.
+-- | Render your React application into the DOM.  Use this from your @main@ function, and only in the browser.
 reactRender :: Typeable props
             => String -- ^ The ID of the HTML element to render the application into.
                       -- (This string is passed to @document.getElementById@)
@@ -113,12 +127,46 @@
     js_ReactRender e (toJSString htmlId)
 
 foreign import javascript unsafe
-    "React['render']($1, document.getElementById($2))"
+    "(typeof ReactDOM === 'object' ? ReactDOM : React)['render']($1, document.getElementById($2))"
     js_ReactRender :: ReactElementRef -> JSString -> IO ()
 
 #else
 
 reactRender _ _ _ = return ()
+
+#endif
+
+-- | Render your React application to a string using either @React.renderToString@ if the first
+-- argument is false or @React.renderToStaticMarkup@ if the first argument is true.
+-- Use this only on the server when running with node.
+reactRenderToString :: Typeable props
+                    => Bool -- ^ Render to static markup?  If true, this won't create extra DOM attributes
+                            -- that React uses internally.
+                    -> ReactView props -- ^ A single instance of this view is created
+                    -> props -- ^ the properties to pass to the view
+                    -> IO Text
+
+#ifdef __GHCJS__
+
+reactRenderToString includeStatic rc props = do
+    (e, _) <- mkReactElement id (return []) $ view rc props mempty
+    sRef <- (if includeStatic then js_ReactRenderStaticMarkup else js_ReactRenderToString) e
+    --return sRef
+    --return $ JSS.unpack sRef
+    mtxt <- fromJSRef sRef
+    maybe (error "Unable to convert string return to Text") return mtxt
+
+foreign import javascript unsafe
+    "(typeof ReactDOM === 'object' ? ReactDOM : React)['renderToString']($1)"
+    js_ReactRenderToString :: ReactElementRef -> IO JSRef
+
+foreign import javascript unsafe
+    "(typeof ReactDOM === 'object' ? ReactDOM : React)['renderToStaticMarkup']($1)"
+    js_ReactRenderStaticMarkup :: ReactElementRef -> IO JSRef
+
+#else
+
+reactRenderToString _ _ _ = return ""
 
 #endif
 
diff --git a/src/React/Flux/Addons/Bootstrap.hs b/src/React/Flux/Addons/Bootstrap.hs
--- a/src/React/Flux/Addons/Bootstrap.hs
+++ b/src/React/Flux/Addons/Bootstrap.hs
@@ -15,11 +15,18 @@
 -- | A bootstrap <http://react-bootstrap.github.io/components.html component>.  For example,
 --
 -- >bootstrap_ "Alert" [ "bsStyle" $= "danger"
--- >                   , callback "onDismiss" (const $ dispatch CloseAlert)
+-- >                   , callback "onDismiss" $ dispatch CloseAlert
 -- >                   ] $
 -- >    p_ "Hello, World!"
+-- >
+-- >bootstrap_ "Nav" [ "activeKey" @= (1 :: Int)
+-- >                 , callback "onSelect" $ \(i :: Int) -> dispatch $ TabChange i
+-- >                 ] $ do
+-- >    bootstrap_ "NavItem" ["eventKey" @= (1 :: Int)] "Item 1"
+-- >    bootstrap_ "NavItem" ["eventKey" @= (2 :: Int)] "Item 2"
+-- >    bootstrap_ "NavItem" ["eventKey" @= (3 :: Int)] "Item 3"
 bootstrap_ :: String
-           -- ^ The component name.   Uses @window['ReactBootstrap'][name]@ to find the class, so
+           -- ^ The component name.   Uses @window[\'ReactBootstrap\'][name]@ to find the class, so
            -- the name can be anything exported to the @window.ReactBoostrap@ object.
            -> [PropertyOrHandler eventHandler]
            -- ^ Properties and callbacks to pass to the ReactBootstrap class.  You can use 'callback'
diff --git a/src/React/Flux/Addons/Intl.hs b/src/React/Flux/Addons/Intl.hs
new file mode 100644
--- /dev/null
+++ b/src/React/Flux/Addons/Intl.hs
@@ -0,0 +1,582 @@
+-- | Bindings to the <http://formatjs.io/react/ ReactIntl> library, which allows easy formatting of
+-- numbers, dates, times, relative times, pluralization, and translated messages. This library can
+-- be used for formatting and pluralization even if you intend to present your application in a single
+-- language and locale.
+--
+-- These bindings are for the 2.0 version of ReactIntl which is currently just a
+-- pre-release, and due to how react-flux works, it also requires React 0.14.  For temporary documentation,
+-- see <https://github.com/yahoo/react-intl/issues/162 issue62>.  Because this is a binding to a
+-- pre-release, the API below might change!  I consider it unlikely, but if a change is required I will
+-- violate the PVP and not increment the major number, just the minor number.
+--
+-- To use these bindings, you need to provide the @ReactIntl@ variable.  In the browser you can just
+-- load the @react-intl.min.js@ script onto the page so that @window.ReactIntl@ exists.  If you are
+-- running in node, execute @ReactIntl = require(\"ReactIntl\");@ so that @global.ReactIntl@
+-- exists.  When compiling with closure, protect the ReactIntl variable as follows:
+--
+-- >(function(global, React, ReactDOM, ReactIntl) {
+-- >contents of all.js
+-- >})(window, window['React'], window['ReactDOM'], window['ReactIntl']);
+--
+-- __Using with a single locale and no translations__.  If you intend to present your application in
+-- a single language, you can still use this module for formatting.  Add a call to 'intlProvider_'
+-- to the top of your app with a hard-coded locale and @Nothing@ for the messages.  You can then use
+-- anything in the /Formatting/ section like 'int_', 'relativeTo_', and 'message', where 'message'
+-- will just always use the default message provided in the source code (helpful for templating).
+-- If you want to specify the locale so dates and numbers are formatted in the user's locale, it is
+-- strongly recommended to set the locale from the server based on the @Accept-Language@ header
+-- and/or a user setting so that the page as a whole is consistint.  I have the server set a
+-- variable on @window@ for the locale to use, and then pass that locale into 'intlProvider_'.
+--
+-- __Translations__.  The react-intl philosophy is that messages should be defined in the source
+-- code instead of kept in a separate file.  To support translations, a tool (in this case Template
+-- Haskell) is used to extract the messages from the source code into a file given to the
+-- translators.  The result of the translation is then used to replace the default message given in
+-- the source code.
+--
+--   1. Use the functions in the /Formatting/ section like 'int_', 'relativeTo_', and 'message'
+--   inside your rendering functions.
+--
+--   2. At the bottom of each file which contains messages, add a call to 'writeIntlMessages'.  This
+--   is a template haskell function which during compilation will produce a file containing all the
+--   messages found within the haskell module.
+--
+--   3. Give these message files to your translators.  The translation results will then need to be
+--   converted into javascript files in the format expected by ReactIntl, which is a javascript
+--   object with keys the 'MessageId's and value the translated message.  For example, each translation
+--   could result in a javascript file such as the following:
+--
+--       @
+--       window.myMessages = window.myMessages || {};
+--       window.myMessages["fr-FR"] = {
+--          "num_photos": "{name} {numPhotos, plural, =0 {n'a pas pris de photographie.} =1 {a pris une photographie.} other {a pris # photographies.}",
+--          ...
+--       };
+--       @
+--
+--   4. Based on the @Accept-Language@ header and/or a user setting, the server includes the
+--   appropriate translation javascript file and sets a variable on window containing the locale to
+--   use.  Note that no translation javascript file is needed if the default messages from the
+--   source code should be used.
+--
+--        @
+--        \<script type="text\/javascript"\>window.myIntialConfig = { "locale": "fr-FR" };\<\/script\>
+--        \<script src="path\/to\/translations.fr-FR.js"\>\<\/script\>
+--        @
+--
+--   5. Add a call to 'intlProvider_' at the top of your application, passing the locale and the
+--   messages.
+--
+--        @
+--        foreign import javascript unsafe
+--          "$r = window[\'myInitialConfig\'][\'locale\']"
+--          js_initialLocale :: JSString
+--
+--        foreign import javascript unsafe
+--          "window[\'myMessages\'] ? window[\'myMessages\'][$1] : null"
+--          js_myMessages :: JSString -> JSRef
+--
+--        myApp :: ReactView ()
+--        myApp = defineView "my application" $ \() -> do
+--            intlProvider_ (JSString.unpack js_initialLocale) (Just $ js_myMessages js_initialLocale) $
+--              ...
+--        @
+--
+--        If you want to allow changing the locale without a page refresh, just load the initial
+--        locale into a store and use a controller-view to pass the locale and lookup the messages
+--        for 'intlProvider_'.
+
+{-# LANGUAGE TemplateHaskell, QuasiQuotes, OverloadedStrings #-}
+module React.Flux.Addons.Intl(
+    intlProvider_
+
+  -- * Formatting
+  
+  -- ** Numbers
+  , int_
+  , double_
+  , formattedNumber_
+
+  -- ** Dates and Times
+  , DayFormat(..)
+  , shortDate
+  , day_
+  , TimeFormat(..)
+  , shortDateTime
+  , utcTime_
+  , formattedDate_
+
+  -- ** Relative Times
+  , relativeTo_
+  , formattedRelative_
+
+  -- ** Plural
+  , plural_
+
+  -- ** Messages
+  , MessageId
+  , message
+  , message'
+  , htmlMsg
+  , htmlMsg'
+
+  -- * Translation
+  , Message(..)
+  , writeIntlMessages
+  , intlFormatJson
+  , intlFormatJsonWithoutDescription
+  , intlFormatAndroidXML
+) where
+
+import Control.Monad (when, forM_)
+import Data.Aeson (Object, Value(Object))
+import Data.Char (ord, isPrint)
+import Data.List (sortBy)
+import Data.Maybe (catMaybes, fromMaybe)
+import Data.Monoid ((<>))
+import Data.Ord (comparing)
+import Data.Time
+import Language.Haskell.TH (runIO, Q, Loc, location, ExpQ)
+import Language.Haskell.TH.Syntax (liftString, qGetQ, qPutQ, reportWarning, Dec)
+import React.Flux
+import System.IO (withFile, IOMode(..))
+import qualified Data.Aeson as Aeson
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
+import qualified Data.HashMap.Strict as H
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+
+#ifdef __GHCJS__
+
+import GHCJS.Types (JSRef)
+
+foreign import javascript unsafe
+    "$r = ReactIntl['IntlProvider']"
+    js_intlProvider :: JSRef
+
+foreign import javascript unsafe
+    "$r = ReactIntl['FormattedNumber']"
+    js_formatNumber :: JSRef
+
+foreign import javascript unsafe
+    "$r = ReactIntl['FormattedDate']"
+    js_formatDate :: JSRef
+
+foreign import javascript unsafe
+    "$r = ReactIntl['FormattedRelative']"
+    js_formatRelative :: JSRef
+
+foreign import javascript unsafe
+    "$r = ReactIntl['FormattedPlural']"
+    js_formatPlural :: JSRef
+
+foreign import javascript unsafe
+    "$r = ReactIntl['FormattedMessage']"
+    js_formatMsg :: JSRef
+
+foreign import javascript unsafe
+    "$r = ReactIntl['FormattedHTMLMessage']"
+    js_formatHtmlMsg :: JSRef
+
+foreign import javascript unsafe
+    "$r = (new Date($1, $2-1, $3))"
+    js_mkDate :: Int -> Int -> Int -> JSRef
+
+-- | Convert a day to a javascript Date
+dayToRef :: Day -> JSRef
+dayToRef day = js_mkDate (fromIntegral y) m d
+    where
+        (y, m, d) = toGregorian day
+
+foreign import javascript unsafe
+    "$r = (new Date(Date.UTC($1, $2-1, $3, $4, $5, $6, $7)))"
+    js_mkDateTime :: Int -> Int -> Int -> Int -> Int -> Int -> Int -> JSRef
+
+-- | Convert a UTCTime to a javascript date object.
+timeToRef :: UTCTime -> JSRef
+timeToRef (UTCTime uday time) = js_mkDateTime (fromIntegral year) month day hour minute sec micro
+    where
+        (year, month, day) = toGregorian uday
+        TimeOfDay hour minute pSec = timeToTimeOfDay time
+        (sec, fracSec) = properFraction pSec
+        micro = round $ fracSec * 1000000
+
+#else
+
+type JSRef = ()
+
+js_intlProvider :: JSRef
+js_intlProvider = ()
+
+js_formatNumber :: JSRef
+js_formatNumber = ()
+
+js_formatDate :: JSRef
+js_formatDate = ()
+
+js_formatRelative :: JSRef
+js_formatRelative = ()
+
+js_formatPlural :: JSRef
+js_formatPlural = ()
+
+js_formatMsg :: JSRef
+js_formatMsg = ()
+
+js_formatHtmlMsg :: JSRef
+js_formatHtmlMsg = ()
+
+dayToRef :: Day -> JSRef
+dayToRef _ = ()
+
+timeToRef :: UTCTime -> JSRef
+timeToRef _ = ()
+
+#endif
+
+-- | Use the IntlProvider to set the @locale@, @formats@, and @messages@ property.
+intlProvider_ :: String -- ^ the locale to use
+              -> Maybe JSRef
+                  -- ^ A reference to translated messages, which must be an object with keys
+                  -- 'MessageId' and value the translated message.  Set this as Nothing if you are not using
+                  -- translated messages, since either @Nothing@ or a null JSRef will cause the messages
+                  -- from the source code to be used.
+              -> Maybe Object
+                  -- ^ An object to use for the @formats@ parameter which allows custom formats.  I
+                  -- suggest you use custom formats only for messages.  Custom formats for numbers
+                  -- and dates not in a message is better done by writing a small Haskell utility
+                  -- function wrapping for example 'formattedNumber_'.
+              -> ReactElementM eventHandler a -- ^ The children of this element.  All descendents will use the given locale and messages.
+              -> ReactElementM eventHandler a
+intlProvider_ locale mmsgs mformats = foreignClass js_intlProvider props
+    where
+        props = catMaybes [ Just ("locale" @= locale)
+                          , (property "messages") <$> mmsgs
+                          , (property "formats" . Object) <$> mformats
+                          ]
+
+--------------------------------------------------------------------------------
+--- Numbers
+--------------------------------------------------------------------------------
+
+-- | Format an integer using 'formattedNumber_' and the default style.
+int_ :: Int -> ReactElementM eventHandler ()
+int_ i = formattedNumber_ [ "value" @= i ]
+
+-- | Format a double using 'formattedNumber_' and the default style.
+double_ :: Double -> ReactElementM eventHandler ()
+double_ d = formattedNumber_ [ "value" @= d ]
+
+-- | A <http://formatjs.io/react/#formatted-number FormattedNumber> which allows arbitrary properties
+-- and therefore allows control over the style and format of the number.  The accepted properties are
+-- any options supported by
+-- <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat Intl.NumberFormat>.
+formattedNumber_ :: [PropertyOrHandler eventHandler] -> ReactElementM eventHandler ()
+formattedNumber_ props = foreignClass js_formatNumber props mempty
+
+--------------------------------------------------------------------------------
+-- Date/Time
+--------------------------------------------------------------------------------
+
+-- | How to display a date.  Each non-Nothing component will be displayed while the Nothing
+-- components will be ommitted.  If everything is nothing, then it is assumed that year, month, and
+-- day are each numeric.
+--
+-- These properties coorespond directly the options accepted by
+-- <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat Intl.DateTimeFormat>.
+data DayFormat = DayFormat {
+    weekdayF :: Maybe String -- ^ possible values are narrow, short, and long
+  , eraF :: Maybe String -- ^ possible values are narrow, short, and long
+  , yearF :: Maybe String -- ^ possible values are numeric and 2-digit
+  , monthF :: Maybe String -- ^ possible values are numeric, 2-digit, narrow, short, and long
+  , dayF :: Maybe String -- ^ possible values are numeric and 2-digit
+} deriving Show
+
+-- | Convert a format to the properties accepted by FormattedDate
+dayFtoProps :: DayFormat -> [PropertyOrHandler handler]
+dayFtoProps (DayFormat w e y m d) = catMaybes
+    [ ("weekday"@=) <$> w
+    , ("era"@=) <$> e
+    , ("year"@=) <$> y
+    , ("month"@=) <$> m
+    , ("day"@=) <$> d
+    ]
+
+-- | A short day format, where month is \"short\" and year and day are \"numeric\".
+shortDate :: DayFormat
+shortDate = DayFormat
+  { weekdayF = Nothing
+  , eraF = Nothing
+  , yearF = Just "numeric"
+  , monthF = Just "short"
+  , dayF = Just "numeric"
+  }
+
+-- | How to display a time.  Each non-Nothing component will be displayed while Nothing components
+-- will be ommitted.
+--
+-- These properties coorespond directly the options accepted by
+-- <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat Intl.DateTimeFormat>.
+data TimeFormat = TimeFormat {
+    hourF :: Maybe String -- ^ possible values are numeric and 2-digit
+  , minuteF :: Maybe String -- ^ possible values are numeric and 2-digit
+  , secondF :: Maybe String -- ^ possible values are numeric and 2-digit
+  , timeZoneNameF :: Maybe String -- ^ possible values are short and long
+} deriving Show
+
+-- | Convert a time format to properties for the FormattedDate element
+timeFtoProps :: TimeFormat -> [PropertyOrHandler handler]
+timeFtoProps (TimeFormat h m s t) = catMaybes
+    [ ("hour"@=) <$> h
+    , ("minute"@=) <$> m
+    , ("second"@=) <$> s
+    , ("timeZoneName"@=) <$> t
+    ]
+
+-- | A default date and time format, using 'shortDate' and then numeric for hour, minute, and
+-- second.
+shortDateTime :: (DayFormat, TimeFormat)
+shortDateTime = (shortDate, TimeFormat
+  { hourF = Just "numeric"
+  , minuteF = Just "numeric"
+  , secondF = Just "numeric"
+  , timeZoneNameF = Nothing
+  })
+
+-- | Display a 'Day' in the given format using the @FormattedDate@ class and then wrap it in a
+-- HTML5 <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time time> element.
+day_ :: DayFormat -> Day -> ReactElementM eventHandler ()
+day_ fmt day = time_ [property "dateTime" dateRef] $ foreignClass js_formatDate props mempty
+    where
+        dateRef = dayToRef day
+        props = property "value" dateRef : dayFtoProps fmt
+
+-- | Display a 'UTCTime' using the given format.  Despite giving the time in UTC, it will be
+-- displayed to the user in their current timezone.  In addition, wrap it in a HTML5
+-- <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time time> element.
+utcTime_ :: (DayFormat, TimeFormat) -> UTCTime -> ReactElementM eventHandler ()
+utcTime_ (dayFmt, timeF) t = time_ [property "dateTime" timeRef] $ foreignClass js_formatDate props mempty
+    where
+        timeRef = timeToRef t
+        props = property "value" timeRef : (dayFtoProps dayFmt ++ timeFtoProps timeF)
+
+-- | A raw <http://formatjs.io/react/#formatted-date FormattedDate> class which allows custom
+-- properties to be passed.  The given 'Day' or 'UTCTime' will be converted to a javascript Date
+-- object and passed in the @value@ property.  The remaining properties can be any properties that
+-- <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat Intl.DateTimeFormat>
+-- accepts.  For example, you could pass in \"timeZone\" to specify a specific timezone to display.
+formattedDate_ :: Either Day UTCTime -> [PropertyOrHandler eventHandler] -> ReactElementM eventHandler ()
+formattedDate_ t props = foreignClass js_formatDate (valProp:props) mempty
+    where
+        valProp = property "value" $ either dayToRef timeToRef t
+
+
+-- | Display the 'UTCTime' as a relative time.  In addition, wrap the display in a HTML5
+-- <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time time> element.
+relativeTo_ :: UTCTime -> ReactElementM eventHandler ()
+relativeTo_ t = time_ [property "dateTime" timeRef] $ foreignClass js_formatRelative [property "value" timeRef] mempty
+    where
+        timeRef = timeToRef t
+
+-- | Format the given UTCTime using the <http://formatjs.io/react/#formatted-relative FormattedRelative>
+-- class to display a relative time to now.  The given 'UTCTime' is passed in the value property.
+-- The supported style/formatting properties are \"units\" which can be one of second, minute, hour,
+-- day, month, or year and \"style\" which if given must be numeric.
+formattedRelative_ :: UTCTime -> [PropertyOrHandler eventHandler] -> ReactElementM eventHandler ()
+formattedRelative_ t props = foreignClass js_formatRelative (property "value" (timeToRef t) : props) mempty
+
+--------------------------------------------------------------------------------
+-- Plural
+--------------------------------------------------------------------------------
+
+-- | A simple plural formatter useful if you do not want the full machinery of messages.  This does
+-- not support translation, for that you must use messages which via the ICU message syntax support
+-- pluralization.  The properties passed to 'plural_' must be @value@, and then at least one of the
+-- properties from @other@, @zero@, @one@, @two@, @few@, @many@.
+plural_ :: [PropertyOrHandler eventHandler] -> ReactElementM eventHandler ()
+plural_ props = foreignClass js_formatPlural props mempty
+
+--------------------------------------------------------------------------------
+-- Messages
+--------------------------------------------------------------------------------
+
+-- | An identifier for a message, must be globally unique.
+type MessageId = T.Text
+
+-- | A message.
+data Message = Message {
+    msgDescription :: T.Text -- ^ A description intended to provide context for translators.
+  , msgDefaultMsg :: T.Text -- ^ The default message written in <http://formatjs.io/guides/message-syntax/ ICU message syntax>.
+} deriving Show
+
+-- | This is the type stored in the Q monad with qGetQ and qPutQ
+type MessageMap = H.HashMap MessageId (Message, Loc)
+
+-- | Utility function to build the properties for FormattedMessage.
+messageToProps :: MessageId -> Message -> [PropertyOrHandler eventHandler] -> [PropertyOrHandler eventHandler]
+messageToProps i (Message desc m) props = ["id" @= i, "description" @= desc, "defaultMessage" @= m, nestedProperty "values" props]
+
+-- | Render a message and also record it during compilation.  This template haskell
+-- splice produces an expression of type @[PropertyOrHandler eventHandler] -> ReactElementM eventHandler
+-- ()@, which should be passed the values for the message.  For example,
+--
+-- >li_ ["id" $= "some-id"] $
+-- >    $(message "num_photos" "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} {takenAgo}.")
+-- >        [ "name" $= "Neil Armstrong"
+-- >        , "numPhotos" @= (100 :: Int)
+-- >        , elementProperty "takenAgo" $ relativeTo_ (UTCTime (fromGregorian 1969 7 20) (2*60*60 + 56*60))
+-- >        ]
+--
+-- This will first lookup the 'MessageId' (in this case @num_photos@) in the  @messages@ parameter passed to 'intlProvider_'.
+-- If no messages were passed, 'intlProvider_' was not called, or the 'MessageId' was not found, the default message is used.
+--
+-- In my project, I create a wrapper around 'message' which sets the 'MessageId' as the sha1 hash of
+-- the message.  I did not implement it in react-flux because I did not want to add cryptohash as a
+-- dependency.  For example,
+--
+-- >import Crypto.Hash (hash, SHA1)
+-- >import qualified Data.Text as T
+-- >import qualified Data.Text.Encoding as T
+-- >
+-- >msg :: T.Text -> ExpQ
+-- >msg txt = message (T.pack $ show (hash (T.encodeUtf8 txt) :: Digest SHA1)) txt
+message :: MessageId
+        -> T.Text -- ^ The default message written in <http://formatjs.io/guides/message-syntax/ ICU message syntax>.
+                  -- This message is used if no translation is found, and is also the message given to the translators.
+        -> ExpQ --Q (TExp ([PropertyOrHandler eventHandler] -> ReactElementM eventHandler ()))
+message ident m = formattedMessage [|js_formatMsg|] ident $ Message "" m
+
+-- | Similar to 'message' but use a @FormattedHTMLMessage@ which allows HTML inside the message.  It
+-- is recomended that you instead use 'message' together with 'elementProperty' to include rich text
+-- inside the message.  This splice produces a value of type @[PropertyOrHandler
+-- eventHandler] -> ReactElementM eventHandler ()@, the same as 'message'.
+htmlMsg :: MessageId
+        -> T.Text -- ^ default message written in ICU message syntax
+        -> ExpQ
+htmlMsg ident m = formattedMessage [|js_formatHtmlMsg|] ident $ Message "" m
+
+-- | A variant of 'message' which allows you to specify some context for translators.
+message' :: MessageId
+         -> T.Text -- ^ A description indented to provide context for translators
+         -> T.Text -- ^ The default message written in ICU message syntax
+         -> ExpQ --Q (TExp ([PropertyOrHandler eventHandler] -> ReactElementM eventHandler ()))
+message' ident descr m = formattedMessage [|js_formatMsg|] ident $ Message descr m
+
+-- | A variant of 'htmlMsg' that allows you to specify some context for translators.
+htmlMsg' :: MessageId
+         -> T.Text -- ^ A description intended to provide context for translators
+         -> T.Text -- ^ The default message written in ICU message syntax
+         -> ExpQ
+htmlMsg' ident descr m = formattedMessage [|js_formatHtmlMsg|] ident $ Message descr m
+
+-- | Utility function for messages
+formattedMessage :: ExpQ -> MessageId -> Message -> ExpQ --Q (TExp ([PropertyOrHandler eventHandler] -> ReactElementM eventHandler ()))
+formattedMessage cls ident m = do
+    curLoc <- location
+    mmap :: MessageMap <- fromMaybe H.empty <$> qGetQ
+    case H.lookup ident mmap of
+        Just (prevMsg, prevLoc) | msgDefaultMsg m /= msgDefaultMsg prevMsg -> do
+            reportWarning $ unlines
+                [ "Message with id " ++ (T.unpack ident) ++ " appears twice with different messages"
+                , show curLoc ++ ": " ++ (T.unpack $ msgDefaultMsg m)
+                , show prevLoc ++ ": " ++ (T.unpack $ msgDefaultMsg prevMsg)
+                ]
+        _ -> return ()
+    qPutQ $ H.insert ident (m, curLoc) mmap
+
+    let liftText x = [| T.pack $(liftString $ T.unpack x)|]
+        liftedMsg = [| Message $(liftText $ msgDescription m) $(liftText $ msgDefaultMsg m) |]
+    [|\vals -> foreignClass $cls (messageToProps $(liftText ident) $liftedMsg vals) mempty |]
+
+-- | Perform an arbitrary IO action on the accumulated messages at compile time, which usually
+-- should be to write the messages to a file.  Despite producing a value of type @Q [Dec]@,
+-- no declarations are produced.  Instead, this is purly to allow IO to happen.  A call to this
+-- function should be placed at the bottom of the file, since it only will output messages that
+-- appear above the call.  Also, to provide consistency, I suggest you create a utility wrapper
+-- around this function.  For example,
+--
+-- >{-# LANGUAGE TemplateHaskell #-}
+-- >module MessageUtil where
+-- >
+-- >import Language.Haskell.TH
+-- >import Language.Haskell.TH.Syntax
+-- >import React.Flux.Addons.Intl
+-- >
+-- >writeMessages :: String -> Q [Dec]
+-- >writeMessages name = writeIntlMessages (intlFormatJson $ "some/diretory/" ++ name ++ ".json")
+--
+-- Note that all paths in template haskell are relative to the directory containing the @.cabal@
+-- file.  You can then use this as follows:
+--
+-- >{-# LANGUAGE TemplateHaskell #-}
+-- >module SomeViews where
+-- >
+-- >import React.Flux
+-- >import React.Flux.Addons.Intl
+-- >import MessageUtil
+-- >
+-- >someView :: ReactView ()
+-- >someView = defineView .... use $(message) in render ...
+-- >
+-- >anotherView :: ReactView ()
+-- >anotherView = defineView ... use $(message) in render ...
+-- >
+-- >writeMessages "some-views"
+writeIntlMessages :: (H.HashMap MessageId Message -> IO ()) -> Q [Dec]
+writeIntlMessages f = do
+    mmap :: MessageMap <- fromMaybe H.empty <$> qGetQ
+    runIO $ f $ fmap fst mmap
+    return []
+
+-- | Format messages as json.  The format is an object where keys are the 'MessageId's, and the
+-- value is an object with two properties, @message@ and optionally @description@.  This happens to
+-- the the same format as <https://developer.chrome.com/extensions/i18n-messages chrome>, although
+-- the syntax of placeholders uses ICU message syntax instead of chrome's syntax.  This does not
+-- pretty-print the JSON, but I suggest before adding these messages in source control you pretty
+-- print and sort by MessageIds so diffs are easy to read.  This can be done with the
+-- @aeson-pretty@ package, but I did not want to add it as a dependency.
+intlFormatJson :: FilePath -> H.HashMap MessageId Message -> IO ()
+intlFormatJson fp mmap = BL.writeFile fp $ Aeson.encode $ Aeson.Object $ fmap f mmap
+    where
+        f (Message "" m) = Aeson.object [(Aeson..=) "message" m]
+        f (Message desc m) = Aeson.object [(Aeson..=) "message" m, (Aeson..=) "description" desc]
+
+-- | Format messages as json, ignoring the description.  The format is an object where the keys are
+-- the 'MessageId's and the value is the message string.  This format is used by many javascript
+-- libraries, so many translation tools exist.
+intlFormatJsonWithoutDescription :: FilePath -> H.HashMap MessageId Message -> IO ()
+intlFormatJsonWithoutDescription fp mmap = BL.writeFile fp $ Aeson.encode $ Aeson.Object $ fmap f mmap
+    where
+        f (Message _ m) = Aeson.String m
+
+-- | Format messages in <http://developer.android.com/guide/topics/resources/string-resource.html Android XML>
+-- format, but just using strings.  String arrays and plurals are handled in the ICU message,
+-- instead of in the XML.  There are many utilities to translate these XML messages, and the format has the
+-- advantage that it can include the descriptions as well as the messages.  Also, the messages are
+-- sorted by 'MessageId' so that if the output is placed in source control the diffs are easy to
+-- review.
+intlFormatAndroidXML :: FilePath -> H.HashMap MessageId Message -> IO ()
+intlFormatAndroidXML fp mmap = withFile fp WriteMode $ \handle -> do
+    B.hPut handle "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+    B.hPut handle "<resources>\n"
+    let putText = B.hPut handle . T.encodeUtf8
+
+    let msgs = sortBy (comparing fst) $ H.toList mmap
+    forM_ msgs $ \(ident, m) -> do
+        when (msgDescription m /= "") $
+            putText $ "<!-- " <> escapeForXml (msgDescription m) <> " -->\n"
+        -- TODO: escape!!
+        putText $ "<string name=\"" <> escapeForXml ident <> "\">" <> escapeForXml (msgDefaultMsg m) <> "</string>\n"
+    B.hPut handle "</resources>\n"
+
+escapeForXml :: T.Text -> T.Text
+escapeForXml = T.concatMap f
+    where
+        f '<' = "&lt;"
+        f '>' = "&gt;"
+        f '&' = "&amp;"
+        f '"' = "&quot;"
+        f '\'' = "&apos;"
+        f x | isPrint x || x == '\n' = T.singleton x
+        f x = "&#" <> T.pack (show $ ord x) <> ";"
diff --git a/src/React/Flux/Addons/React.hs b/src/React/Flux/Addons/React.hs
--- a/src/React/Flux/Addons/React.hs
+++ b/src/React/Flux/Addons/React.hs
@@ -5,8 +5,6 @@
 module React.Flux.Addons.React (
   -- * Animation
     cssTransitionGroup
-  , CSSTransitionProps(..)
-  , defaultTransitionProps
 
   -- * Perf
   , PerfAction(..)
@@ -17,7 +15,6 @@
 
 import Control.DeepSeq (NFData)
 import Control.Monad (forM_)
-import Data.Typeable (Typeable)
 import GHC.Generics (Generic)
 import React.Flux
 
@@ -25,35 +22,15 @@
 import GHCJS.Types (JSRef, JSString)
 #endif
 
--- | The properties for the CSS Transition Group.
-data CSSTransitionProps = CSSTransitionProps
-    { transitionName :: String
-    , transitionAppear :: Bool
-    , transitionEnter :: Bool
-    , transitionLeave :: Bool
-    } deriving (Typeable)
-
--- | Default properties for CSS Transition Group, using \"react-transition\" as the transition name.
-defaultTransitionProps :: CSSTransitionProps
-defaultTransitionProps = CSSTransitionProps
-    { transitionName = "react-transition"
-    , transitionAppear = False
-    , transitionEnter = True
-    , transitionLeave = True
-    }
-
--- | The <https://facebook.github.io/react/docs/animation.html ReactCSSTransitionGroup> element.  At
--- the moment, only the high-level API is supported.
-cssTransitionGroup :: CSSTransitionProps -> ReactElementM eventHandler a -> ReactElementM eventHandler a
+-- | The <https://facebook.github.io/react/docs/animation.html ReactCSSTransitionGroup> element.
+-- For example in React 0.14,
+--
+-- >cssTransitionGroup ["transitionName" $= "example", transitionAppear @= True, transitionAppearTimeout @= (100 :: Int)] $
+-- >    h1_ "Fading at initial mount"
+cssTransitionGroup :: [PropertyOrHandler eventHandler] -> ReactElementM eventHandler a -> ReactElementM eventHandler a
 
 #ifdef __GHCJS__
-cssTransitionGroup p children = foreignClass js_CSSTransitionGroup props children
-    where
-        props = [ "transitionName" @= transitionName p
-                , "transitionAppear" @= transitionAppear p
-                , "transitionEnter" @= transitionEnter p
-                , "transitionLeave" @= transitionLeave p
-                ]
+cssTransitionGroup props children = foreignClass js_CSSTransitionGroup props children
 
 foreign import javascript unsafe
     "React['addons']['CSSTransitionGroup']"
diff --git a/src/React/Flux/DOM.hs b/src/React/Flux/DOM.hs
--- a/src/React/Flux/DOM.hs
+++ b/src/React/Flux/DOM.hs
@@ -189,6 +189,7 @@
 node(defs)
 node(ellipse)
 node(g)
+node(image)
 node(line)
 node(linearGradient)
 node(mask)
@@ -327,6 +328,7 @@
 defs_ :: Term eventHandler arg result => arg -> result; defs_ = term "defs"
 ellipse_ :: Term eventHandler arg result => arg -> result; ellipse_ = term "ellipse"
 g_ :: Term eventHandler arg result => arg -> result; g_ = term "g"
+image_ :: Term eventHandler arg result => arg -> result; image_ = term "image"
 line_ :: Term eventHandler arg result => arg -> result; line_ = term "line"
 linearGradient_ :: Term eventHandler arg result => arg -> result; linearGradient_ = term "linearGradient"
 mask_ :: Term eventHandler arg result => arg -> result; mask_ = term "mask"
diff --git a/src/React/Flux/Internal.hs b/src/React/Flux/Internal.hs
--- a/src/React/Flux/Internal.hs
+++ b/src/React/Flux/Internal.hs
@@ -29,11 +29,13 @@
 #ifdef __GHCJS__
 import           Unsafe.Coerce
 import qualified Data.JSString as JSS
+import           JavaScript.Array (JSArray)
 import qualified JavaScript.Array as JSA
 import           GHCJS.Foreign.Callback
 import qualified JavaScript.Object as JSO
 import           GHCJS.Types (JSRef, JSString, IsJSRef, jsref)
-import           GHCJS.Marshal (ToJSRef(..), fromJSRef)
+import           GHCJS.Marshal (ToJSRef(..))
+import           GHCJS.Foreign (jsNull)
 import           React.Flux.Export
 #else
 import Data.Text (Text)
@@ -42,7 +44,9 @@
 class ToJSRef a
 instance ToJSRef Value
 instance ToJSRef Text
+instance ToJSRef ()
 class IsJSRef a
+type JSArray = JSRef
 #endif
 
 -- type JSObject a = JSO.Object a
@@ -71,25 +75,30 @@
       { propertyName :: String
       , propertyVal :: ref
       }
+ | NestedProperty
+      { nestedPropertyName :: String
+      , nestedPropertyVals :: [PropertyOrHandler handler]
+      }
  | ElementProperty
       { elementPropertyName :: String
       , elementValue :: ReactElementM handler ()
       }
- | EventHandler
-      { evtHandlerName :: String
-      , evtHandler :: HandlerArg -> handler
+ | CallbackPropertyWithArgumentArray
+      { caPropertyName :: String
+      , caFunc :: JSArray -> IO handler
       }
- | CallbackProperty
-      { callbackName :: String
-      , callbackFn :: Value -> handler
+ | CallbackPropertyWithSingleArgument
+      { csPropertyName :: String
+      , csFunc :: HandlerArg -> handler
       }
 
 instance Functor PropertyOrHandler where
     fmap _ (Property name val) = Property name val
+    fmap f (NestedProperty name vals) = NestedProperty name (map (fmap f) vals)
     fmap f (ElementProperty name (ReactElementM mkElem)) =
         ElementProperty name $ ReactElementM $ mapWriter (\((),e) -> ((), fmap f e)) mkElem
-    fmap f (EventHandler name h) = EventHandler name (f . h)
-    fmap f (CallbackProperty name g) = CallbackProperty name (f . g)
+    fmap f (CallbackPropertyWithArgumentArray name h) = CallbackPropertyWithArgumentArray name (fmap f . h)
+    fmap f (CallbackPropertyWithSingleArgument name h) = CallbackPropertyWithSingleArgument name (f . h)
 
 -- | Create a property from anything that can be converted to a JSRef
 property :: ToJSRef val => String -> val -> PropertyOrHandler handler
@@ -245,7 +254,7 @@
                 [x] -> return x
                 xs -> lift $ do
                     emptyObj <- JSO.create
-                    let arr = JSA.fromList $ map reactElementRef xs
+                    let arr = jsref $ JSA.fromList $ map reactElementRef xs
                     js_ReactCreateElementName "div" emptyObj arr
 
         -- add the property or handler to the javascript object
@@ -253,21 +262,27 @@
         addPropOrHandlerToObj obj (Property n val) = lift $ do
             vRef <- toJSRef val
             JSO.setProp (toJSString n) vRef obj
+        addPropOrHandlerToObj obj (NestedProperty n vals) = do
+            nested <- lift $ JSO.create
+            mapM_ (addPropOrHandlerToObj nested) vals
+            lift $ JSO.setProp (toJSString n) (jsref nested) obj
         addPropOrHandlerToObj obj (ElementProperty name rM) = do
             ReactElementRef ref <- mToElem rM
             lift $ JSO.setProp (toJSString name) ref obj
-        addPropOrHandlerToObj obj (EventHandler str handler) = do
+        addPropOrHandlerToObj obj (CallbackPropertyWithArgumentArray name func) = do
             -- this will be released by the render function of the class (jsbits/class.js)
-            cb <- lift $ syncCallback1 ContinueAsync $ \evtRef ->
-                runHandler $ handler $ HandlerArg evtRef
-            tell [cb]
-            lift $ JSO.setProp (toJSString str) (jsref cb) obj
-        addPropOrHandlerToObj obj (CallbackProperty str handler) = do
             cb <- lift $ syncCallback1 ContinueAsync $ \argref -> do
-                v <- fromJSRef argref
-                runHandler $ handler $ maybe (error "Unable to decode callback value") id v
+                handler <- func $ unsafeCoerce argref
+                runHandler handler
             tell [cb]
-            lift $ JSO.setProp (toJSString str) (jsref cb) obj
+            wrappedCb <- lift $ js_CreateArgumentsCallback cb
+            lift $ JSO.setProp (toJSString name) wrappedCb obj
+        addPropOrHandlerToObj obj (CallbackPropertyWithSingleArgument name func) = do
+            -- this will be released by the render function of the class (jsbits/class.js)
+            cb <- lift $ syncCallback1 ContinueAsync $ \ref ->
+                runHandler $ func $ HandlerArg ref
+            tell [cb]
+            lift $ JSO.setProp (toJSString name) (jsref cb) obj
 
         -- call React.createElement
         createElement :: ReactElement eventHandler -> MkReactElementM [ReactElementRef]
@@ -279,20 +294,28 @@
             obj <- lift $ JSO.create
             mapM_ (addPropOrHandlerToObj obj) $ fProps f
             childNodes <- createElement $ fChild f
-            let childArr = JSA.fromList $ map reactElementRef childNodes
+            let children = case map reactElementRef childNodes of
+                             [] -> jsNull
+                             [x] -> x
+                             xs -> jsref $ JSA.fromList xs
             e <- lift $ case fName f of
-                Left s -> js_ReactCreateElementName (toJSString s) obj childArr
-                Right ref -> js_ReactCreateForeignElement ref obj childArr
+                Left s -> js_ReactCreateElementName (toJSString s) obj children
+                Right ref -> js_ReactCreateForeignElement ref obj children
             return [e]
         createElement (ViewElement { ceClass = rc, ceProps = props, ceKey = mkey, ceChild = child }) = do
             childNodes <- createElement child
             propsE <- lift $ export props -- this will be released inside the lifetime events for the class (jsbits/class.js)
-            let arr = JSA.fromList $ map reactElementRef childNodes
+
+            let children = case map reactElementRef childNodes of
+                             [] -> jsNull
+                             [x] -> x
+                             xs -> jsref $ JSA.fromList xs
+
             e <- lift $ case mkey of
                 Just key -> do
                     keyRef <- toKeyRef key
-                    js_ReactCreateKeyedElement rc keyRef propsE arr
-                Nothing -> js_ReactCreateClass rc propsE arr
+                    js_ReactCreateKeyedElement rc keyRef propsE children
+                Nothing -> js_ReactCreateClass rc propsE children
             return [e]
 
 type MkReactElementM a = WriterT [Callback (JSRef -> IO ())] IO a
@@ -303,19 +326,23 @@
 
 foreign import javascript unsafe
     "React['createElement']($1, $2, $3)"
-    js_ReactCreateElementName :: JSString -> JSO.Object -> JSA.JSArray -> IO ReactElementRef
+    js_ReactCreateElementName :: JSString -> JSO.Object -> JSRef -> IO ReactElementRef
 
 foreign import javascript unsafe
     "React['createElement']($1, $2, $3)"
-    js_ReactCreateForeignElement :: ReactViewRef a -> JSO.Object -> JSA.JSArray -> IO ReactElementRef
+    js_ReactCreateForeignElement :: ReactViewRef a -> JSO.Object -> JSRef -> IO ReactElementRef
 
 foreign import javascript unsafe
     "React['createElement']($1, {hs:$2}, $3)"
-    js_ReactCreateClass :: ReactViewRef a -> Export props -> JSA.JSArray -> IO ReactElementRef
+    js_ReactCreateClass :: ReactViewRef a -> Export props -> JSRef -> IO ReactElementRef
 
 foreign import javascript unsafe
     "React['createElement']($1, {key: $2, hs:$3}, $4)"
-    js_ReactCreateKeyedElement :: ReactViewRef a -> JSRef -> Export props -> JSA.JSArray -> IO ReactElementRef
+    js_ReactCreateKeyedElement :: ReactViewRef a -> JSRef -> Export props -> JSRef -> IO ReactElementRef
+
+foreign import javascript unsafe
+    "hsreact$mk_arguments_callback($1)"
+    js_CreateArgumentsCallback :: Callback (JSRef -> IO ()) -> IO JSRef
 
 js_ReactCreateContent :: String -> ReactElementRef
 js_ReactCreateContent = ReactElementRef . unsafeCoerce . toJSString
diff --git a/src/React/Flux/PropertiesAndEvents.hs b/src/React/Flux/PropertiesAndEvents.hs
--- a/src/React/Flux/PropertiesAndEvents.hs
+++ b/src/React/Flux/PropertiesAndEvents.hs
@@ -1,12 +1,15 @@
--- | This module contains the definitions for creating properties to pass to elements, as well as
--- the definitions for the
+-- | This module contains the definitions for creating properties to pass to javascript elements and
+-- foreign javascript classes.  In addition, it contains definitions for the
 -- <https://facebook.github.io/react/docs/events.html React Event System>.
+{-# LANGUAGE UndecidableInstances #-}
 module React.Flux.PropertiesAndEvents (
     PropertyOrHandler
   , (@=)
   , ($=)
   , property
   , elementProperty
+  , nestedProperty
+  , CallbackFunction
   , callback
 
   -- * Events
@@ -17,6 +20,7 @@
   , preventDefault
   , stopPropagation
   , capturePhase
+  , on
 
   -- * Keyboard
   , KeyboardEvent(..)
@@ -85,6 +89,7 @@
 
 import           React.Flux.Internal
 import           React.Flux.Store
+import           React.Flux.Views (ViewEventHandler, StatefulViewEventHandler)
 
 #ifdef __GHCJS__
 import           Data.Maybe (fromMaybe)
@@ -121,11 +126,63 @@
 elementProperty :: String -> ReactElementM handler () -> PropertyOrHandler handler
 elementProperty = ElementProperty
 
+-- | Allows you to create nested object properties.  The list of properties passed in will be
+-- converted to an object which is then set as the value for a property with the given name.  For
+-- example,
+--
+-- >[ nestedProperty "Hello" [ "a" @= (100 :: Int), "b" $= "World" ]
+-- >, "c" $= "!!!"
+-- >]
+--
+-- would create a javascript object
+--
+-- >{"Hello": {a: 100, b: "World"}, "c": "!!!"}
+nestedProperty :: String -> [PropertyOrHandler handler] -> PropertyOrHandler handler
+nestedProperty = NestedProperty
+
+-- | A class which is used to implement <https://wiki.haskell.org/Varargs variable argument functions>.
+-- Any function where each argument implements 'FromJSRef' and the result is either
+-- 'ViewEventHandler' or 'StatefulViewEventHandler' is an instance of this class.
+class CallbackFunction handler a | a -> handler  where
+    applyFromArguments :: JSArray -> Int -> a -> IO handler
+
+instance CallbackFunction ViewEventHandler ViewEventHandler where
+    applyFromArguments _ _ h = return h
+
+instance CallbackFunction (StatefulViewEventHandler s) (StatefulViewEventHandler s) where
+    applyFromArguments _ _ h = return h
+
+instance (FromJSRef a, CallbackFunction handler b) => CallbackFunction handler (a -> b) where
+#if __GHCJS__
+    applyFromArguments args k f = do
+        ma <- fromJSRef $ if k >= JSA.length args then nullRef else JSA.index k args
+        a <- maybe (error "Unable to decode callback argument") return ma
+        applyFromArguments args (k+1) $ f a
+#else
+    applyFromArguments _ _ _ = error "Not supported in GHC"
+#endif
+
 -- | Create a callback property.  This is primarily intended for foreign React classes which expect
 -- callbacks to be passed to them as properties.  For events on DOM elements, you should instead use
 -- the handlers below.
-callback :: String -> (A.Value -> handler) -> PropertyOrHandler handler
-callback = CallbackProperty
+--
+-- The function @func@ can be any function, as long as each argument to the function is an instance
+-- of 'FromJSRef' and the result of the function is @handler@.  Internally, 'callback' creates a
+-- javascript function which accesses the @arguments@ javascript object and then matches entries in
+-- @arguments@ to the parameters of @func@.  If @func@ has more parameters than the javascript
+-- @arguments@ object, a javascript null is used for the conversion.  Since the 'Maybe' instance of
+-- 'FromJSRef' converts a null reference to 'Nothing', you can exploit this to create
+-- variable-argument javascript callbacks.
+--
+-- For example, all three of the following functions could be passed as @func@ inside a view.
+--
+-- >foo :: Int -> Maybe String -> ViewEventHandler
+-- >bar :: Aeson.Value -> ViewEventHandler
+-- >baz :: ViewEventHandler
+--
+-- For another example, see the haddock comments in "React.Flux.Addons.Bootstrap".
+callback :: CallbackFunction handler func => String -> func -> PropertyOrHandler handler
+callback name func = CallbackPropertyWithArgumentArray name $ \arr -> applyFromArguments arr 0 func
 
 ----------------------------------------------------------------------------------------------------
 --- Generic Event
@@ -184,25 +241,24 @@
     , evtHandlerArg = arg
     }
 
--- | Create an event handler from a name and a handler function.
---
--- This can also be used to pass in arbitrary callbacks to foreign javascript React classes.
--- Indeed, what 'on' does is create a callback and then add a property with key the string passed to
--- 'on' and value the callback.
-on :: String -> (HandlerArg -> handler) -> PropertyOrHandler handler
-on = EventHandler
+-- | Use this to create an event handler for an event not covered by the rest of this module.  At
+-- the moment, this is just the media events (onPlay, onPause, etc.) on image and video tags.
+on :: String -> (Event -> handler) -> PropertyOrHandler handler
+on name f = CallbackPropertyWithSingleArgument
+    { csPropertyName = name
+    , csFunc = f . parseEvent
+    }
 
 -- | Construct a handler from a detail parser, used by the various events below.
-mkHandler :: String -- ^ The event name
-          -> (HandlerArg -> detail) -- ^ A function parsing the details for the specific event.
-          -> (Event -> detail -> handler) -- ^ The function implementing the handler.
-          -> PropertyOrHandler handler
-mkHandler name parseDetail f = EventHandler
-    { evtHandlerName = name
-    , evtHandler = \raw -> f (parseEvent raw) (parseDetail raw)
+on2 :: String -- ^ The event name
+    -> (HandlerArg -> detail) -- ^ A function parsing the details for the specific event.
+    -> (Event -> detail -> handler) -- ^ The function implementing the handler.
+    -> PropertyOrHandler handler
+on2 name parseDetail f = CallbackPropertyWithSingleArgument
+    { csPropertyName = name
+    , csFunc = \raw -> f (parseEvent raw) (parseDetail raw)
     }
 
-
 -- | In a hack, the prevent default and stop propagation are actions since that is the easiest way
 -- of allowing users to specify these actions (IO is not available in view event handlers).  We
 -- create a fake store to handle these actions.
@@ -262,7 +318,7 @@
 -- | By default, the handlers below are triggered during the bubbling phase.  Use this to switch
 -- them to trigger during the capture phase.
 capturePhase :: PropertyOrHandler handler -> PropertyOrHandler handler
-capturePhase (EventHandler n h) = EventHandler (n ++ "Capture") h
+capturePhase (CallbackPropertyWithSingleArgument n h) = CallbackPropertyWithSingleArgument (n ++ "Capture") h
 capturePhase _ = error "You must use React.Flux.PropertiesAndEvents.capturePhase on an event handler"
 
 ---------------------------------------------------------------------------------------------------
@@ -311,13 +367,13 @@
     }
 
 onKeyDown :: (Event -> KeyboardEvent -> handler) -> PropertyOrHandler handler
-onKeyDown = mkHandler "onKeyDown" parseKeyboardEvent
+onKeyDown = on2 "onKeyDown" parseKeyboardEvent
 
 onKeyPress :: (Event -> KeyboardEvent -> handler) -> PropertyOrHandler handler
-onKeyPress = mkHandler "onKeyPress" parseKeyboardEvent
+onKeyPress = on2 "onKeyPress" parseKeyboardEvent
 
 onKeyUp :: (Event -> KeyboardEvent -> handler) -> PropertyOrHandler handler
-onKeyUp = mkHandler "onKeyUp" parseKeyboardEvent
+onKeyUp = on2 "onKeyUp" parseKeyboardEvent
 
 --------------------------------------------------------------------------------
 -- Focus Events
@@ -331,10 +387,10 @@
 parseFocusEvent (HandlerArg ref) = FocusEvent $ EventTarget $ js_getProp ref "relatedTarget"
 
 onBlur :: (Event -> FocusEvent -> handler) -> PropertyOrHandler handler
-onBlur = mkHandler "onBlur" parseFocusEvent
+onBlur = on2 "onBlur" parseFocusEvent
 
 onFocus :: (Event -> FocusEvent -> handler) -> PropertyOrHandler handler
-onFocus = mkHandler "onFocus" parseFocusEvent
+onFocus = on2 "onFocus" parseFocusEvent
 
 --------------------------------------------------------------------------------
 -- Form Events
@@ -343,13 +399,13 @@
 -- | The onChange event is special in React and should be used for all input change events.  For
 -- details, see <https://facebook.github.io/react/docs/forms.html>
 onChange :: (Event -> handler) -> PropertyOrHandler handler
-onChange f = on "onChange" (f . parseEvent)
+onChange = on "onChange"
 
 onInput :: (Event -> handler) -> PropertyOrHandler handler
-onInput f = on "onInput" (f . parseEvent)
+onInput = on "onInput"
 
 onSubmit :: (Event -> handler) -> PropertyOrHandler handler
-onSubmit f = on "onSubmit" (f . parseEvent)
+onSubmit = on "onSubmit"
 
 --------------------------------------------------------------------------------
 -- Mouse Events
@@ -395,66 +451,67 @@
     }
 
 onClick :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onClick = mkHandler "onClick" parseMouseEvent
+onClick = on2 "onClick" parseMouseEvent
 
 onContextMenu :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onContextMenu = mkHandler "onContextMenu" parseMouseEvent
+onContextMenu = on2 "onContextMenu" parseMouseEvent
 
 onDoubleClick :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDoubleClick = mkHandler "onDoubleClick" parseMouseEvent
+onDoubleClick = on2 "onDoubleClick" parseMouseEvent
 
 onDrag :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDrag = mkHandler "onDrag" parseMouseEvent
+onDrag = on2 "onDrag" parseMouseEvent
 
 onDragEnd :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDragEnd = mkHandler "onDragEnd" parseMouseEvent
+onDragEnd = on2 "onDragEnd" parseMouseEvent
 
 onDragEnter :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDragEnter = mkHandler "onDragEnter" parseMouseEvent
+onDragEnter = on2 "onDragEnter" parseMouseEvent
 
 onDragExit :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDragExit = mkHandler "onDragExit" parseMouseEvent
+onDragExit = on2 "onDragExit" parseMouseEvent
 
 onDragLeave :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDragLeave = mkHandler "onDragLeave" parseMouseEvent
+onDragLeave = on2 "onDragLeave" parseMouseEvent
 
 onDragOver :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDragOver = mkHandler "onDragOver" parseMouseEvent
+onDragOver = on2 "onDragOver" parseMouseEvent
 
 onDragStart :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDragStart = mkHandler "onDragStart" parseMouseEvent
+onDragStart = on2 "onDragStart" parseMouseEvent
 
 onDrop :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onDrop = mkHandler "onDrop" parseMouseEvent
+onDrop = on2 "onDrop" parseMouseEvent
 
 onMouseDown :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onMouseDown = mkHandler "onMouseDown" parseMouseEvent
+onMouseDown = on2 "onMouseDown" parseMouseEvent
 
 onMouseEnter :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onMouseEnter = mkHandler "onMouseEnter" parseMouseEvent
+onMouseEnter = on2 "onMouseEnter" parseMouseEvent
 
 onMouseLeave :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onMouseLeave = mkHandler "onMouseLeave" parseMouseEvent
+onMouseLeave = on2 "onMouseLeave" parseMouseEvent
 
 onMouseMove :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onMouseMove = mkHandler "onMouseMove" parseMouseEvent
+onMouseMove = on2 "onMouseMove" parseMouseEvent
 
 onMouseOut :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onMouseOut = mkHandler "onMouseOut" parseMouseEvent
+onMouseOut = on2 "onMouseOut" parseMouseEvent
 
 onMouseOver :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onMouseOver = mkHandler "onMouseOver" parseMouseEvent
+onMouseOver = on2 "onMouseOver" parseMouseEvent
 
 onMouseUp :: (Event -> MouseEvent -> handler) -> PropertyOrHandler handler
-onMouseUp = mkHandler "onMouseUp" parseMouseEvent
+onMouseUp = on2 "onMouseUp" parseMouseEvent
 
 --------------------------------------------------------------------------------
 -- Touch
 --------------------------------------------------------------------------------
 
+-- | Initialize touch events is only needed with React 0.13, in version 0.14 it was removed.
 #ifdef __GHCJS__
 foreign import javascript unsafe
-    "React['initializeTouchEvents'](true)"
+    "React['initializeTouchEvents'] ? React['initializeTouchEvents'](true) : null"
     initializeTouchEvents :: IO ()
 #else
 initializeTouchEvents :: IO ()
@@ -519,23 +576,23 @@
     }
 
 onTouchCancel :: (Event -> TouchEvent -> handler) -> PropertyOrHandler handler
-onTouchCancel = mkHandler "onTouchCancel" parseTouchEvent
+onTouchCancel = on2 "onTouchCancel" parseTouchEvent
 
 onTouchEnd :: (Event -> TouchEvent -> handler) -> PropertyOrHandler handler
-onTouchEnd = mkHandler "onTouchEnd" parseTouchEvent
+onTouchEnd = on2 "onTouchEnd" parseTouchEvent
 
 onTouchMove :: (Event -> TouchEvent -> handler) -> PropertyOrHandler handler
-onTouchMove = mkHandler "onTouchMove" parseTouchEvent
+onTouchMove = on2 "onTouchMove" parseTouchEvent
 
 onTouchStart :: (Event -> TouchEvent -> handler) -> PropertyOrHandler handler
-onTouchStart = mkHandler "onTouchStart" parseTouchEvent
+onTouchStart = on2 "onTouchStart" parseTouchEvent
 
 --------------------------------------------------------------------------------
 -- UI Events
 --------------------------------------------------------------------------------
 
 onScroll :: (Event -> handler) -> PropertyOrHandler handler
-onScroll f = on "onScroll" (f . parseEvent)
+onScroll = on "onScroll"
 
 --------------------------------------------------------------------------------
 -- Wheel
@@ -557,9 +614,9 @@
     }
 
 onWheel :: (Event -> MouseEvent -> WheelEvent -> handler) -> PropertyOrHandler handler
-onWheel f = EventHandler
-    { evtHandlerName = "onWheel"
-    , evtHandler = \raw -> f (parseEvent raw) (parseMouseEvent raw) (parseWheelEvent raw)
+onWheel f = CallbackPropertyWithSingleArgument
+    { csPropertyName = "onWheel"
+    , csFunc = \raw -> f (parseEvent raw) (parseMouseEvent raw) (parseWheelEvent raw)
     }
 
 --------------------------------------------------------------------------------
@@ -567,10 +624,10 @@
 --------------------------------------------------------------------------------
 
 onLoad :: (Event -> handler) -> PropertyOrHandler handler
-onLoad f = on "onLoad" (f . parseEvent)
+onLoad = on "onLoad"
 
 onError :: (Event -> handler) -> PropertyOrHandler handler
-onError f = on "onError" (f . parseEvent)
+onError = on "onError"
 
 --------------------------------------------------------------------------------
 --- JS Utils
@@ -584,7 +641,7 @@
 
 foreign import javascript unsafe
     "$1[$2]"
-    js_getArrayProp :: JSRef -> JSString -> JSA.JSArray
+    js_getArrayProp :: JSRef -> JSString -> JSArray
 
 -- | Access a property from an object.  Since event objects are immutable, we can use
 -- unsafePerformIO without worry.
diff --git a/src/React/Flux/Store.hs b/src/React/Flux/Store.hs
--- a/src/React/Flux/Store.hs
+++ b/src/React/Flux/Store.hs
@@ -28,7 +28,7 @@
 instance IsJSRef (ReactStoreRef storeData)
 
 -- | A store contains application state, receives actions from the dispatcher, and notifies
--- component views to re-render themselves.  You can have multiple stores; it should be the case
+-- controller-views to re-render themselves.  You can have multiple stores; it should be the case
 -- that all of the state required to render the page is contained in the stores.  A store keeps a
 -- global reference to a value of type @storeData@, which must be an instance of 'StoreData'.
 --
diff --git a/src/React/Flux/Views.hs b/src/React/Flux/Views.hs
--- a/src/React/Flux/Views.hs
+++ b/src/React/Flux/Views.hs
@@ -185,7 +185,7 @@
 
 -- | A stateful view is a re-usable component of the page which keeps track of internal state.
 -- Try to keep as many views as possible stateless.  The React documentation on
--- <https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html inteactivity and dynamic UIs>
+-- <https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html interactivity and dynamic UIs>
 -- has some discussion of what should and should not go into the state.
 --
 -- The rendering function is a pure function of the state and the properties from the parent.  The
@@ -285,19 +285,22 @@
     js_ReactGetProps :: ReactThis state props -> IO (Export props)
 
 foreign import javascript unsafe
-    "$1['props']['children']"
-    js_ReactGetChildren :: ReactThis state props -> IO (JSArray)
+    "hsreact$children_to_array($1['props']['children'])"
+    js_ReactGetChildren :: ReactThis state props -> IO JSArray
 
 foreign import javascript unsafe
     "$1._updateAndReleaseState($2)"
     js_ReactUpdateAndReleaseState :: ReactThis state props -> Export state -> IO ()
 
+-- React 0.13 has React.findDOMNode, while 0.14 moves it to ReactDOM.findDOMNode.  Also, 0.14
+-- does not need to call findDOMNode on refs.
+
 foreign import javascript unsafe
-    "React['findDOMNode']($1)"
+    "typeof ReactDOM === 'object' ? ReactDOM['findDOMNode']($1) : React['findDOMNode']($1)"
     js_ReactFindDOMNode :: ReactThis state props -> IO JSRef
 
 foreign import javascript unsafe
-    "React['findDOMNode']($1['refs'][$2])"
+    "typeof ReactDOM === 'object' ? $1['refs'][$2] : React['findDOMNode']($1['refs'][$2])"
     js_ReactGetRef :: ReactThis state props -> JSString -> IO JSRef
 
 newtype RenderCbArg = RenderCbArg JSRef
@@ -348,8 +351,7 @@
     node <- render state props
 
     let getPropsChildren = do childRef <- js_ReactGetChildren this
-                              let childArr = toList childRef
-                              return $ map ReactElementRef childArr
+                              return $ map ReactElementRef $ toList childRef
 
     (element, evtCallbacks) <- mkReactElement (runHandler this) getPropsChildren node
 
diff --git a/test/client/TestClient.hs b/test/client/TestClient.hs
--- a/test/client/TestClient.hs
+++ b/test/client/TestClient.hs
@@ -1,21 +1,22 @@
-{-# LANGUAGE OverloadedStrings, TypeFamilies #-}
-module Main where
+{-# LANGUAGE OverloadedStrings, TypeFamilies, ScopedTypeVariables, DeriveAnyClass, FlexibleInstances, DeriveGeneric #-}
+module TestClient (testClient) where
 
+import Control.DeepSeq (NFData)
 import Control.Monad
+import Data.Monoid ((<>))
 import Data.Typeable (Typeable)
 import Data.Maybe
 import Debug.Trace
+import GHC.Generics (Generic)
 import React.Flux
 import React.Flux.Lifecycle
 import React.Flux.Internal (toJSString)
+import React.Flux.Addons.React
+import React.Flux.Addons.Bootstrap
 
 import GHCJS.Types (JSRef, JSString)
 import GHCJS.Marshal (fromJSRef)
 
--- TODO: 
--- * Addons
--- * callback property
-
 foreign import javascript unsafe
     "(function(x) { \
     \    if (!window.test_client_output) window.test_client_output = []; \
@@ -105,7 +106,7 @@
                 ]
                 "Testing preventDefault"
 
-        p_ $
+        div_ $
             div_ [ "id" $= "outer-div"
                  , onClick $ \_ _ -> output ["Click on outer div"]
                  , capturePhase $ onDoubleClick $ \e _ -> output ["Double click outer div"] ++ [stopPropagation e]
@@ -156,7 +157,6 @@
         button_ [ "id" $= "increment-state"
                 , onClick $ \_ _ st -> ([], Just $ st + 1)
                 ] "Incr"
-        div_ childrenPassedToView
 
     , lComponentWillMount = Just $ \pAndS setStateFn -> do
         outputIO ["will mount"]
@@ -192,14 +192,114 @@
 testLifecycle_ s = view testLifecycle s $ span_ ["id" $= "child-passed-to-view"] "I am a child!!!"
 
 --------------------------------------------------------------------------------
+--- Children passed to view
+--------------------------------------------------------------------------------
+
+displayChildren :: ReactView String
+displayChildren = defineView "display children" $ \ident ->
+    span_ ["className" $= "display-children", "id" @= ident] childrenPassedToView
+
+displayChildren_ :: String -> ReactElementM handler () -> ReactElementM handler ()
+displayChildren_ = view displayChildren
+
+displayChildrenSpec :: ReactElementM handler ()
+displayChildrenSpec = ul_ $ do
+    li_ $ displayChildren_ "empty-children" mempty
+    li_ $ displayChildren_ "single-child-wrapper" $ span_ ["id" $= "single-child"] "Single Child!!"
+    li_ $ displayChildren_ "multi-child" $ span_ ["id" $= "child1"] "Child 1" <> span_ ["id" $= "child2"] "Child 2"
+
+--------------------------------------------------------------------------------
+--- CSS Transitions
+--------------------------------------------------------------------------------
+
+cssTransitions :: ReactView [String]
+cssTransitions = defineView "css transitions" $ \items ->
+    div_ ["id" $= "css-transitions"] $
+        cssTransitionGroup ["transitionName" $= "example"] $
+            forM_ (zip items [(0 :: Int)..]) $ \(txt, key) ->
+                div_ ["key" @= key] $ span_ ["className" $= "css-transition-entry"] $ elemText txt
+
+--------------------------------------------------------------------------------
+--- Bootstrap
+--------------------------------------------------------------------------------
+
+bootstrapSpec :: ReactView ()
+bootstrapSpec = defineView "bootstrap" $ \() -> div_ ["id" $= "bootstrap"] $ do
+    bootstrap_ "Alert" [ "bsStyle" $= "danger"
+                       , callback "onDismiss" $ output ["Closing alert"]
+                       ] $
+        p_ "Hello, World!"
+
+    bootstrap_ "Nav" [ "activeKey" @= (1 :: Int)
+                     , callback "onSelect" $ \(i :: Int) -> output ["Switched to " ++ show i]
+                     ] $ do
+        bootstrap_ "NavItem" ["eventKey" @= (1 :: Int)] "Item 1"
+        bootstrap_ "NavItem" ["eventKey" @= (2 :: Int)] "Item 2"
+        bootstrap_ "NavItem" ["eventKey" @= (3 :: Int)] "Item 3"
+
+--------------------------------------------------------------------------------
+--- shouldComponentUpdate
+--------------------------------------------------------------------------------
+
+data ShouldComponentUpdateData = ShouldComponentUpdateData Int String
+    deriving (Typeable, Show)
+
+data ShouldComponentUpdateAction = IncrementAllSCUData
+                                 | IncrementFirstSCUData
+                                 | NoChangeToSCUData
+    deriving (Show, Typeable, Generic, NFData)
+
+instance StoreData [ShouldComponentUpdateData] where
+    type StoreAction [ShouldComponentUpdateData] = ShouldComponentUpdateAction
+    transform NoChangeToSCUData s = return s
+    transform IncrementAllSCUData d = return [ShouldComponentUpdateData (i+1) s | ShouldComponentUpdateData i s <- d]
+    transform IncrementFirstSCUData (ShouldComponentUpdateData i s:xs) = return $ (ShouldComponentUpdateData (i+1) s) : xs
+    transform _ [] = error "Should never happen"
+
+shouldComponentUpdateStore :: ReactStore [ShouldComponentUpdateData]
+shouldComponentUpdateStore = mkStore [ ShouldComponentUpdateData 1 "Hello"
+                                     , ShouldComponentUpdateData 2 "World"
+                                     , ShouldComponentUpdateData 3 "!!!"
+                                     ]
+
+-- | This will log wheenver componentWillUpdate lifecycle event occurs
+logComponentWillUpdate :: ReactView ShouldComponentUpdateData
+logComponentWillUpdate = defineLifecycleView "shouldComponentUpdate spec" () lifecycleConfig
+    { lRender = \() (ShouldComponentUpdateData i s) ->
+                    span_ (elemShow i) <> span_ (elemText s)
+    , lComponentWillUpdate = Just $ \curProps _ (ShouldComponentUpdateData newI newS) () -> do
+          ShouldComponentUpdateData curI curS <- lGetProps curProps
+          outputIO [ "Component will update"
+                   , "current props: " ++ show curI ++ " " ++ curS
+                   , "new props: " ++ show newI ++ " " ++ newS
+                   ]
+    }
+
+shouldComponentUpdateSpec :: ReactView ()
+shouldComponentUpdateSpec = defineControllerView "should component update" shouldComponentUpdateStore $ \ds () -> 
+    div_ ["id" $= "should-component-update"] $ do
+        ul_ $ forM_ (zip ds [(0 :: Int)..]) $ \(su, i)  ->
+            li_ $ viewWithKey logComponentWillUpdate i su mempty
+
+        button_ ["id" $= "no-change-scu", onClick $ \_ _ -> [SomeStoreAction shouldComponentUpdateStore NoChangeToSCUData]]
+            "No change to data"
+
+        button_ ["id" $= "change-all-scu", onClick $ \_ _ -> [SomeStoreAction shouldComponentUpdateStore IncrementAllSCUData]]
+            "Increment all"
+
+        button_ ["id" $= "increment-first-scu", onClick $ \_ _ -> [SomeStoreAction shouldComponentUpdateStore IncrementFirstSCUData]]
+            "Increment first entry's integer"
+
+--------------------------------------------------------------------------------
 --- Main
 --------------------------------------------------------------------------------
 
 -- | Test a lifecycle view with all lifecycle methods nothing
-app :: ReactView ()
-app = defineLifecycleView "app" "Hello" lifecycleConfig
+testClient :: ReactView ()
+testClient = defineLifecycleView "app" "Hello" lifecycleConfig
     { lRender = \s () -> do
         eventsView_
+
         when (s /= "") $
             testLifecycle_ s
         button_ [ "id" $= "add-app-str"
@@ -209,9 +309,12 @@
         button_ [ "id" $= "clear-app-str"
                 , onClick $ \_ _ _ -> ([], Just "")
                 ] "Clear"
-    }
 
-main :: IO ()
-main = do
-    initializeTouchEvents
-    reactRender "app" app ()
+        displayChildrenSpec
+
+        view cssTransitions ["A", "B"] mempty
+
+        view bootstrapSpec () mempty
+
+        view shouldComponentUpdateSpec () mempty
+    }
diff --git a/test/client/TestClient13.hs b/test/client/TestClient13.hs
new file mode 100644
--- /dev/null
+++ b/test/client/TestClient13.hs
@@ -0,0 +1,9 @@
+module Main (main) where
+
+import TestClient
+import React.Flux
+
+main :: IO ()
+main = do
+    initializeTouchEvents
+    reactRender "app" testClient ()
diff --git a/test/client/TestClient14.hs b/test/client/TestClient14.hs
new file mode 100644
--- /dev/null
+++ b/test/client/TestClient14.hs
@@ -0,0 +1,93 @@
+{-# LANGUAGE OverloadedStrings, TypeFamilies, ScopedTypeVariables, TemplateHaskell #-}
+module Main (main) where
+
+import Data.Time
+import React.Flux
+import React.Flux.Addons.Intl
+import GHCJS.Types (JSRef)
+
+import TestClient
+
+--------------------------------------------------------------------------------
+--- Intl
+--------------------------------------------------------------------------------
+
+foreign import javascript unsafe
+    "{'with_trans': 'message from translation {abc}'}"
+    js_translations :: JSRef
+
+intlSpec :: ReactView ()
+intlSpec = defineView "intl" $ \() -> div_ ["id" $= "intl-spec"] $ intlProvider_ "en-US" (Just js_translations) Nothing $
+    ul_ $ do
+        li_ ["id" $= "f-number"] $
+            formattedNumber_ [ "value" @= (0.9 :: Double), "style" $= "percent" ]
+        li_ ["id" $= "f-int"] $ int_ 100000
+        li_ ["id" $= "f-double"] $ double_ 40000.2
+
+        let moon = fromGregorian 1969 7 20
+            fullDayF = DayFormat { weekdayF = Just "long", eraF = Just "short", yearF = Just "2-digit", monthF = Just "long", dayF = Just "2-digit" }
+
+        li_ ["id" $= "f-shortday"] $ day_ shortDate moon
+        li_ ["id" $= "f-fullday"] $ day_ fullDayF moon
+        li_ ["id" $= "f-date"] $ formattedDate_ (Left moon)
+                [ "weekday" $= "short", "month" $= "short", "day" $= "numeric", "year" $= "2-digit" ]
+
+        let step = UTCTime moon (2*60*60 + 56*60) -- 1969-7-20 02:56 UTC
+            fullT = (fullDayF, TimeFormat { hourF = Just "numeric", minuteF = Just "2-digit", secondF = Just "numeric", timeZoneNameF = Just "long" })
+        
+        li_ ["id" $= "f-shorttime"] $ utcTime_ shortDateTime step
+        li_ ["id" $= "f-fulltime"] $ utcTime_ fullT step
+        li_ ["id" $= "f-time"] $ formattedDate_ (Right step)
+                [ "year" $= "2-digit", "month" $= "short", "day" $= "numeric"
+                , "hour" $= "numeric", "minute" $= "2-digit", "second" $= "numeric"
+                , "timeZoneName" $= "short"
+                , "timeZone" $= "Pacific/Tahiti"
+                ]
+
+        {-
+        li_ ["id" $= "f-relative"] $ relativeTo_ step
+        li_ ["id" $= "f-relative-days"] $ formattedRelative_ step [ "units" $= "day" ]
+        -}
+
+        li_ ["id" $= "f-plural"] $ plural_ [ "value" @= (100 :: Int), "one" $= "plural one", "other" $= "plural other"]
+
+        li_ ["id" $= "f-msg"] $
+            $(message "photos" "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} {takenAgo}.")
+                [ "name" $= "Neil Armstrong"
+                , "numPhotos" @= (100 :: Int)
+                , elementProperty "takenAgo" $ span_ ["id" $= "takenAgoSpan"] "years ago"
+                ]
+
+        li_ ["id" $= "f-msg-with-trans"] $
+            $(message "with_trans" "this is not used {abc}") ["abc" $= "xxx"]
+
+        li_ ["id" $= "f-msg-with-descr"] $
+            $(message' "photos2" "How many photos?" "{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}}.")
+                [ "name" $= "Neil Armstrong"
+                , "numPhotos" @= (0 :: Int)
+                ]
+
+        li_ ["id" $= "f-html-msg"] $
+            $(htmlMsg "html1" "<b>{num}</b> is the answer to life, the universe, and everything")
+                [ "num" @= (42 :: Int) ]
+
+        li_ ["id" $= "f-html-msg-with-descr"] $
+            $(htmlMsg' "html2" "Hitchhiker's Guide" "{num} is the <b>answer</b> to life, the universe, and everything")
+                [ "num" @= (42 :: Int) ]
+
+--------------------------------------------------------------------------------
+--- Main
+--------------------------------------------------------------------------------
+
+-- | Test a lifecycle view with all lifecycle methods nothing
+app :: ReactView ()
+app = defineView "main app" $ \() -> do
+    view testClient () mempty
+    view intlSpec () mempty
+
+main :: IO ()
+main = reactRender "app" app ()
+
+writeIntlMessages (intlFormatJson "test/client/msgs/jsonmsgs.json")
+writeIntlMessages (intlFormatJsonWithoutDescription "test/client/msgs/jsonnodescr.json")
+writeIntlMessages (intlFormatAndroidXML "test/client/msgs/android.xml")
diff --git a/test/client/msgs/android.xml b/test/client/msgs/android.xml
new file mode 100644
--- /dev/null
+++ b/test/client/msgs/android.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<string name="html1">&lt;b&gt;{num}&lt;/b&gt; is the answer to life, the universe, and everything</string>
+<!-- Hitchhiker&apos;s Guide -->
+<string name="html2">{num} is the &lt;b&gt;answer&lt;/b&gt; to life, the universe, and everything</string>
+<string name="photos">{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} {takenAgo}.</string>
+<!-- How many photos? -->
+<string name="photos2">{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}}.</string>
+<string name="with_trans">this is not used {abc}</string>
+</resources>
diff --git a/test/client/msgs/jsonmsgs.json b/test/client/msgs/jsonmsgs.json
new file mode 100644
--- /dev/null
+++ b/test/client/msgs/jsonmsgs.json
@@ -0,0 +1,1 @@
+{"photos":{"message":"{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} {takenAgo}."},"html2":{"message":"{num} is the <b>answer</b> to life, the universe, and everything","description":"Hitchhiker's Guide"},"photos2":{"message":"{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}}.","description":"How many photos?"},"html1":{"message":"<b>{num}</b> is the answer to life, the universe, and everything"},"with_trans":{"message":"this is not used {abc}"}}
diff --git a/test/client/msgs/jsonnodescr.json b/test/client/msgs/jsonnodescr.json
new file mode 100644
--- /dev/null
+++ b/test/client/msgs/jsonnodescr.json
@@ -0,0 +1,1 @@
+{"photos":"{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}} {takenAgo}.","html2":"{num} is the <b>answer</b> to life, the universe, and everything","photos2":"{name} took {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}}.","html1":"<b>{num}</b> is the answer to life, the universe, and everything","with_trans":"this is not used {abc}"}
diff --git a/test/client/test-client.html b/test/client/test-client.html
deleted file mode 100644
--- a/test/client/test-client.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<html>
-    <head>
-        <title>Test Client</title>
-    </head>
-    <body>
-        <div id="app"></div>
-        <script src="https://fb.me/react-with-addons-0.13.3.js"></script>
-        <script src="../../dist/build/test-client/test-client.jsexe/all.js"></script>
-    </body>
-</html>
diff --git a/test/client/test-client13.html b/test/client/test-client13.html
new file mode 100644
--- /dev/null
+++ b/test/client/test-client13.html
@@ -0,0 +1,12 @@
+<html>
+    <head>
+        <title>Test Client</title>
+    </head>
+    <body>
+        <div id="app"></div>
+        <script src="https://fb.me/react-with-addons-0.13.3.js"></script>
+        <script src="node_modules/react-intl/dist/react-intl.js"></script>
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.25.2/react-bootstrap.min.js"></script>
+        <script src="../../dist/build/test-client-13/test-client-13.jsexe/all.js"></script>
+    </body>
+</html>
diff --git a/test/client/test-client14.html b/test/client/test-client14.html
new file mode 100644
--- /dev/null
+++ b/test/client/test-client14.html
@@ -0,0 +1,13 @@
+<html>
+    <head>
+        <title>Test Client</title>
+    </head>
+    <body>
+        <div id="app"></div>
+        <script src="https://fb.me/react-with-addons-0.14.0-rc1.js"></script>
+        <script src=" https://fb.me/react-dom-0.14.0-rc1.js"></script>
+        <script src="node_modules/react-intl/dist/react-intl.js"></script>
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/react-bootstrap/0.25.2/react-bootstrap.min.js"></script>
+        <script src="../../dist/build/test-client-14/test-client-14.jsexe/all.js"></script>
+    </body>
+</html>
diff --git a/test/spec/Main.hs b/test/spec/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/spec/Main.hs
@@ -0,0 +1,10 @@
+module Main where
+
+import Test.Hspec
+import System.Process
+import qualified Spec
+
+main :: IO ()
+main = do
+    _ <- system "cd ../../example/todo && make"
+    hspec Spec.spec
diff --git a/test/spec/Spec.hs b/test/spec/Spec.hs
--- a/test/spec/Spec.hs
+++ b/test/spec/Spec.hs
@@ -1,1 +1,1 @@
-{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
diff --git a/test/spec/TestClientSpec.hs b/test/spec/TestClientSpec.hs
--- a/test/spec/TestClientSpec.hs
+++ b/test/spec/TestClientSpec.hs
@@ -5,6 +5,7 @@
 import           Control.Monad
 import           Control.Monad.IO.Class (liftIO)
 import           Data.List
+--import           Data.Time
 import qualified Data.Text              as T
 import           System.Directory       (getCurrentDirectory)
 import           Test.Hspec.WebDriver
@@ -34,11 +35,37 @@
     p <- findElem (ById "world")
     getText p `shouldReturn` (T.pack $ "Current props: " ++ props)
 
+scuShouldBe :: [(Int, String)] -> WD ()
+scuShouldBe items = do
+    d <- findElem $ ById "should-component-update"
+    lis <- findElemsFrom d $ ByTag "li"
+    length lis `shouldBe` length items
+    forM_ (zip lis items) $ \(li,(i, s)) ->
+        getText li `shouldReturn` (T.pack $ show i ++ s)
+
+intlSpanShouldBe :: String -> String -> WD ()
+intlSpanShouldBe ident txt = do
+    e <- findElem (ById $ T.pack ident)
+    getText e `shouldReturn` T.pack txt
+
+-- | Only up to 999,999 since this is just used for the number of days since 1969
+showWithComma :: Integer -> String
+showWithComma i = show x ++ "," ++ show y
+    where
+        (x, y) = divMod i 1000
+
 spec :: Spec
-spec = session " for the test client" $ using Chrome $ do
+spec = do
+    describe "React 0.13" $ testClientSpec "test-client13.html"
+    describe "React 0.14" $ do
+        testClientSpec "test-client14.html"
+        intlSpec "test-client14.html"
+
+testClientSpec :: String -> Spec
+testClientSpec filename = session " for the test client" $ using Chrome $ do
     it "opens the page" $ runWD $ do
         dir <- liftIO $ getCurrentDirectory
-        openPage $ "file://" ++ dir ++ "/../client/test-client.html"
+        openPage $ "file://" ++ dir ++ "/../client/" ++ filename
         loadLog `shouldReturn`
             [ "will mount"
             , "Current props and state: Hello, 12"
@@ -49,8 +76,6 @@
             , "refProps id = world"
             ]
         lifecyclePropsAndStateAre "Hello" 100
-        child <- findElem (ById "child-passed-to-view")
-        isDisplayed child `shouldReturn` True
 
     it "processes a focus event" $ runWD $ do
         findElem (ById "keyinput") >>= click
@@ -158,8 +183,136 @@
                 , "Current props and state: Helloo, 101"
                 ]
 
+    describe "children passed to view" $ do
+
+        it "does not display null children" $ runWD $ do
+            s <- findElem $ ById "empty-children"
+            findElemsFrom s (ByCSS "*") `shouldReturn` []
+            getText s `shouldReturn` ""
+
+        it "displays a single child" $ runWD $ do
+            s <- findElem $ ById "single-child-wrapper"
+            s' <- findElemFrom s $ ByCSS "span#single-child"
+            getText s' `shouldReturn` "Single Child!!"
+
+        it "displays a child list" $ runWD $ do
+            s <- findElem $ ById "multi-child"
+            c1 <- findElemFrom s $ ByCSS "span#child1"
+            getText c1 `shouldReturn` "Child 1"
+            c2 <- findElemFrom s $ ByCSS "span#child2"
+            getText c2 `shouldReturn` "Child 2"
+
+    it "displays the elements inside the transition" $ runWD $ do
+        d <- findElem $ ById "css-transitions"
+        [a, b] <- findElemsFrom d $ ByCSS "span.css-transition-entry"
+        getText a `shouldReturn` "A"
+        getText b `shouldReturn` "B"
+
+    describe "bootstrap" $ do
+
+        it "displays and closes alert" $ runWD $ do
+            alert <- findElem $ ByCSS "div#bootstrap div.alert"
+            (findElemFrom alert (ByTag "p") >>= getText)
+                `shouldReturn` "Hello, World!"
+            findElemFrom alert (ByTag "button") >>= click
+            loadLog `shouldReturn` ["Closing alert"]
+
+        it "switchs nav items" $ runWD $ do
+            navUl <- findElem $ ByCSS "div#bootstrap nav > ul"
+            i1 <- findElemFrom navUl $ ByCSS "li:first-child a"
+            i2 <- findElemFrom navUl $ ByCSS "li:nth-child(2) a"
+            i3 <- findElemFrom navUl $ ByCSS "li:nth-child(3) a"
+            click i2
+            loadLog `shouldReturn` ["Switched to 2"]
+            click i3
+            loadLog `shouldReturn` ["Switched to 3"]
+            click i1
+            loadLog `shouldReturn` ["Switched to 1"]
+
+    describe "should component update" $ do
+
+        it "has the initial data" $ runWD $
+            scuShouldBe [(1, "Hello"), (2, "World"), (3, "!!!")]
+
+        it "increments just the first entry without re-rendering all entries" $ runWD $ do
+            findElem (ById "increment-first-scu") >>= click
+            scuShouldBe [(2, "Hello"), (2, "World"), (3, "!!!")]
+            loadLog `shouldReturn`
+                [ "Component will update"
+                , "current props: 1 Hello"
+                , "new props: 2 Hello"
+                ]
+
+        it "does not update when no change to data" $ runWD $ do
+            findElem (ById "no-change-scu") >>= click
+            scuShouldBe [(2, "Hello"), (2, "World"), (3, "!!!")]
+            loadLog `shouldReturn` []
+
+        it "increments all entries" $ runWD $ do
+            findElem (ById "change-all-scu") >>= click
+            scuShouldBe [(3, "Hello"), (3, "World"), (4, "!!!")]
+            loadLog `shouldReturn`
+                [ "Component will update"
+                , "current props: 2 Hello"
+                , "new props: 3 Hello"
+                , "Component will update"
+                , "current props: 2 World"
+                , "new props: 3 World"
+                , "Component will update"
+                , "current props: 3 !!!"
+                , "new props: 4 !!!"
+                ]
+
+
     {-
     it "inspects the session" $ runWD $ do
         loadLog >>= \x -> liftIO $ putStrLn $ show x
         inspectSession
     -}
+
+intlSpec :: String -> Spec
+intlSpec filename = session " for the i18n test client" $ using Chrome $ do
+    it "opens the page" $ runWD $ do
+        dir <- liftIO $ getCurrentDirectory
+        openPage $ "file://" ++ dir ++ "/../client/" ++ filename
+
+    it "displays the intl formatted data" $ runWD $ do
+        "f-number" `intlSpanShouldBe` "90%"
+        "f-int" `intlSpanShouldBe` "100,000"
+        "f-double" `intlSpanShouldBe` "40,000.2"
+        "f-shortday" `intlSpanShouldBe` "Jul 20, 1969"
+        "f-fullday" `intlSpanShouldBe` "Sunday, July 20, 69 AD"
+        "f-date" `intlSpanShouldBe` "Sun, Jul 20, 69"
+        -- f-shorttime and f-fulltime cannot be (easily) tested since they rely on the current timezone
+        "f-time" `intlSpanShouldBe` "Jul 19, 69, 4:56:00 PM GMT-10"
+        "f-plural" `intlSpanShouldBe` "plural other"
+
+        {-
+        today <- liftIO (utctDay <$> getCurrentTime)
+        let moon = fromGregorian 1969 7 20
+            daysAgo = diffDays today moon
+            yearsAgo :: Int = round $ realToFrac daysAgo / (365 :: Double) -- is close enough
+        "f-relative" `intlSpanShouldBe` (show yearsAgo ++ " years ago")
+        "f-relative-days" `intlSpanShouldBe` (showWithComma daysAgo ++ " days ago")
+        -}
+
+    it "displays messages" $ runWD $ do
+        msg <- findElem $ ById "f-msg"
+        getText msg `shouldReturn` "Neil Armstrong took 100 photos years ago."
+        takenAgoSpan <- findElemFrom msg $ ById "takenAgoSpan"
+        getText takenAgoSpan `shouldReturn` "years ago"
+
+        msg' <- findElem $ ById "f-msg-with-descr"
+        getText msg' `shouldReturn` "Neil Armstrong took no photos."
+
+        "f-msg-with-trans" `intlSpanShouldBe` "message from translation xxx"
+
+        htmlMsg <- findElem $ ById "f-html-msg"
+        getText htmlMsg `shouldReturn` "42 is the answer to life, the universe, and everything"
+        (findElemFrom htmlMsg (ByTag "b") >>= getText)
+            `shouldReturn` "42"
+
+        htmlMsg' <- findElem $ ById "f-html-msg-with-descr"
+        getText htmlMsg' `shouldReturn` "42 is the answer to life, the universe, and everything"
+        (findElemFrom htmlMsg' (ByTag "b") >>= getText)
+            `shouldReturn` "answer"
diff --git a/test/spec/TodoSpec.hs b/test/spec/TodoSpec.hs
--- a/test/spec/TodoSpec.hs
+++ b/test/spec/TodoSpec.hs
@@ -16,7 +16,7 @@
     forM_ (zip entries todos) $ \(li, (todo, complete)) -> do
         chk <- findElemFrom li $ ByCSS "input[type=checkbox]"
         attr chk "checked" `shouldReturn` if complete then Just "true" else Nothing
-        spn <- findElemFrom li $ ByTag "span"
+        spn <- findElemFrom li $ ByTag "label"
         getText spn `shouldReturn` todo
 
     -- check items left
@@ -30,7 +30,7 @@
     -- clear completed
     let completedCnt = length $ filter snd todos
     when (completedCnt > 0) $ do
-        completeBtn <- findElem $ ByCSS "button#clear-completed span"
+        completeBtn <- findElem $ ByCSS "button#clear-completed"
         getText completeBtn `shouldReturn` (T.pack $ "Clear completed (" ++ show completedCnt ++ ")")
 
 getRow :: Int -> WD Element
@@ -58,7 +58,7 @@
 
     it "edits a todo" $ runWD $ do
         midRow <- getRow 1
-        findElemFrom midRow (ByTag "span") >>= moveToCenter
+        findElemFrom midRow (ByTag "label") >>= moveToCenter
         doubleClick
         editBox <- findElemFrom midRow (ByCSS "input.edit")
         sendKeys "Learn react.js" editBox
diff --git a/test/spec/react-flux-spec.cabal b/test/spec/react-flux-spec.cabal
--- a/test/spec/react-flux-spec.cabal
+++ b/test/spec/react-flux-spec.cabal
@@ -6,12 +6,14 @@
 executable react-flux-spec
    ghc-options: -Wall
    hs-source-dirs: .
-   main-is: Spec.hs
-   other-modules: TodoSpec, TestClientSpec
+   main-is: Main.hs
+   other-modules: TodoSpec, TestClientSpec, Spec
    build-depends: base
                 , hspec
                 , webdriver
                 , hspec-webdriver
                 , directory
+                , process
                 , transformers
                 , text
+                , time
diff --git a/test/spec/stack.yaml b/test/spec/stack.yaml
--- a/test/spec/stack.yaml
+++ b/test/spec/stack.yaml
@@ -1,3 +1,3 @@
-resolver: lts-3.2
+resolver: lts-3.6
 packages:
 - '.'
