diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,14 @@
+# 1.2.0
+
+* (Breaking Change) Add timeout support to the AJAX functionality.  This has three changes.  First, the AJAX code moved
+  into it's own module `React.Flux.Ajax` which is not re-exported by `React.Flux`.  Second, the
+  `AjaxRequest` has a new entry `reqTimeout` to optionally specify a timeout value.  Finally, the
+  `jsonAjax` function now takes a `RequestTimeout` as the first parameter.
+
+* `reactRender` and `reactRenderToString` now error when executed if you call them when compiling with GHC
+  instead of GHCJS.  These functions only work when using GHCJS to compile because for the actual rendering
+  we rely on the React javascript code.  In addition, there are a few haddock updates about this change.
+
 # 1.1.1
 
 * When compiling with GHC (not GHCJS), add fake instances for FromJSVal to allow more programs to compile
@@ -15,7 +26,7 @@
   (from the `Data.JSString` module in `ghcjs-base`).  If a value was just going to be passed straight into
   react, I made it a JSString and if the value was intended to be manipulated in Haskell I used Text.
   The main changes are:
-      
+
       * `elemText` used to (confusingly) take a `String`.  Now there are three functions: `elemString`,
         `elemText`, and `elemJSString` that take a `String`, `Text`, and `JSString` value respectively.
 
diff --git a/example/README.md b/example/README.md
--- a/example/README.md
+++ b/example/README.md
@@ -36,7 +36,7 @@
 
 ~~~
 cd example/todo
-npm install react@0.14.6
+npm install react@15.3.0 react-dom@15.3.0
 node run-in-node.js
 ~~~
 
diff --git a/example/purecss-side-menu/index.html b/example/purecss-side-menu/index.html
--- a/example/purecss-side-menu/index.html
+++ b/example/purecss-side-menu/index.html
@@ -9,8 +9,8 @@
     </head>
     <body>
         <div id="side-menu-app"/>
-        <script src="https://fb.me/react-15.1.0.min.js"></script>
-        <script src="https://fb.me/react-dom-15.1.0.min.js"></script>
+        <script src="https://fb.me/react-15.3.0.min.js"></script>
+        <script src="https://fb.me/react-dom-15.3.0.min.js"></script>
         <script src="purecss-side-menu.js"></script>
     </body>
 </html>
diff --git a/example/routing/route-example.html b/example/routing/route-example.html
--- a/example/routing/route-example.html
+++ b/example/routing/route-example.html
@@ -7,7 +7,8 @@
     </head>
     <body>
       <section id="route-example"/>
-      <script src="https://fb.me/react-0.13.3.js"></script>
+      <script src="https://fb.me/react-15.3.0.js"></script>
+      <script src="https://fb.me/react-dom-15.3.0.js"></script>
       <script src="../../js-build/install-root/bin/route-example.jsexe/all.js"></script>
     </body>
 </html>
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,8 +7,8 @@
     </head>
     <body>
         <section id="todoapp"/>
-        <script src="https://fb.me/react-0.15.1.js"></script>
-        <script src=" https://fb.me/react-dom-0.15.1.js"></script>
+        <script src="https://fb.me/react-15.3.0.js"></script>
+        <script src=" https://fb.me/react-dom-15.3.0.js"></script>
         <script src="../../js-build/install-root/bin/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,8 +7,8 @@
     </head>
     <body>
         <section id="todoapp"/>
-        <script src="https://fb.me/react-15.0.1.js"></script>
-        <script src="https://fb.me/react-dom-15.0.1.js"></script>
+        <script src="https://fb.me/react-15.3.0.js"></script>
+        <script src="https://fb.me/react-dom-15.3.0.js"></script>
         <script src="../../js-build/todo.min.js"></script>
     </body>
 </html>
diff --git a/jsbits/ajax.js b/jsbits/ajax.js
--- a/jsbits/ajax.js
+++ b/jsbits/ajax.js
@@ -8,14 +8,25 @@
 
 function hsreact$ajax(req, handler) {
     var xhr = new XMLHttpRequest();
+
     xhr['open'](req['reqMethod'], req['reqURI']);
+
     var lst = req['reqHeaders'];
     for (var i = 0; i < lst.length; i++) {
         xhr['setRequestHeader'](lst[i][0], lst[i][1]);
     }
+
+    if (req['reqTimeout']) {
+        xhr['timeout'] = req['reqTimeout'];
+        xhr['ontimeout'] = function() {
+            hsreact$ajaxCallback(xhr, {hs: handler, timedOut: true});
+        }
+    }
+
     xhr['onreadystatechange'] = function() {
-        if (xhr['readyState'] === XMLHttpRequest['DONE']) {
-            hsreact$ajaxCallback(xhr, {hs: handler});
+        // a status of 0 with DONE occurs when the request is timed out
+        if (xhr['readyState'] === XMLHttpRequest['DONE'] && xhr['status'] !== 0) {
+            hsreact$ajaxCallback(xhr, {hs: handler, timedOut: false});
         }
     };
     xhr['send'](req['reqBody']);
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:             1.1.1
+version:             1.2.0
 synopsis:            A binding to React based on the Flux application architecture for GHCJS
 category:            Web
 homepage:            https://bitbucket.org/wuzzeb/react-flux
@@ -60,6 +60,7 @@
                    React.Flux.PropertiesAndEvents
                    React.Flux.Combinators
                    React.Flux.Lifecycle
+                   React.Flux.Ajax
                    React.Flux.Internal
                    React.Flux.Addons.React
                    React.Flux.Addons.Bootstrap
diff --git a/src/React/Flux.hs b/src/React/Flux.hs
--- a/src/React/Flux.hs
+++ b/src/React/Flux.hs
@@ -131,6 +131,8 @@
 ----------------------------------------------------------------------------------------------------
 
 -- | Render your React application into the DOM.  Use this from your @main@ function, and only in the browser.
+-- 'reactRender' only works when compiled with GHCJS (not GHC), because we rely on the React javascript code
+-- to actually perform the rendering.
 reactRender :: Typeable props
             => String -- ^ The ID of the HTML element to render the application into.
                       -- (This string is passed to @document.getElementById@)
@@ -150,13 +152,19 @@
 
 #else
 
-reactRender _ _ _ = return ()
+reactRender _ _ _ = error "reactRender only works when compiled with GHCJS, because we rely on the javascript React code."
 
 #endif
 
 -- | Render your React application to a string using either @ReactDOMServer.renderToString@ if the first
 -- argument is false or @ReactDOMServer.renderToStaticMarkup@ if the first argument is true.
 -- Use this only on the server when running with node.
+-- 'reactRenderToString' only works when compiled with GHCJS (not GHC), because we rely on the React javascript code
+-- to actually perform the rendering.
+--
+-- If you are interested in isomorphic React, I suggest instead of using 'reactRenderToString' you use
+-- 'exportViewToJavaScript' and then write a small top-level JavaScript view which can then integrate with
+-- all the usual isomorphic React tools.
 reactRenderToString :: Typeable props
                     => Bool -- ^ Render to static markup?  If true, this won't create extra DOM attributes
                             -- that React uses internally.
@@ -184,7 +192,7 @@
 
 #else
 
-reactRenderToString _ _ _ = return ""
+reactRenderToString _ _ _ = error "reactRenderToString only works when compiled with GHCJS, because we rely on the javascript React code."
 
 #endif
 
diff --git a/src/React/Flux/Ajax.hs b/src/React/Flux/Ajax.hs
new file mode 100644
--- /dev/null
+++ b/src/React/Flux/Ajax.hs
@@ -0,0 +1,238 @@
+module React.Flux.Ajax (
+    initAjax
+  , RequestTimeout(..)
+  , jsonAjax
+  , AjaxRequest(..)
+  , AjaxResponse(..)
+  , ajax
+  ) where
+
+import Data.Aeson
+import Data.Text (Text)
+import Data.Typeable (Typeable)
+import GHC.Generics (Generic)
+import React.Flux.Internal
+import React.Flux.Store
+import React.Flux.Views
+
+#ifdef __GHCJS__
+import Control.Arrow ((***))
+import Control.DeepSeq (deepseq)
+import GHCJS.Foreign.Callback
+import GHCJS.Foreign (jsNull)
+import GHCJS.Marshal (ToJSVal(..), toJSVal_aeson, FromJSVal(..))
+import GHCJS.Types (JSVal)
+import qualified Data.Text as T
+import qualified Data.JSString.Text as JSS
+
+import React.Flux.Export
+#endif
+
+-- | An optional timeout to use for @XMLHttpRequest.timeout@.
+-- When a request times out, a status code of 504 is set in 'respStatus' and the
+-- response handler executes.
+data RequestTimeout = TimeoutMilliseconds Int | NoTimeout
+
+instance ToJSVal RequestTimeout where
+    toJSVal (TimeoutMilliseconds i) = toJSVal i
+    toJSVal NoTimeout = pure jsNull
+
+-- | The input to an AJAX request built using @XMLHttpRequest@.
+data AjaxRequest = AjaxRequest
+  { reqMethod :: JSString
+  , reqURI :: JSString
+  , reqTimeout :: RequestTimeout
+  , reqHeaders :: [(JSString, JSString)]
+  , reqBody :: JSVal
+  } deriving (Typeable, Generic)
+
+-- | The response after @XMLHttpRequest@ indicates that the @readyState@ is done.
+data AjaxResponse = AjaxResponse
+  { respStatus :: Int
+  , respResponseText :: JSString
+  , respResponseXHR :: JSVal -- ^ The raw @XMLHttpRequest@ object.  Use this if you want more status about the response,
+                             --   such as response headers.
+  } deriving (Typeable, Generic)
+
+#ifdef __GHCJS__
+-- | GHCJS panics if we export the function directly, so wrap it in a data struct
+data HandlerWrapper = HandlerWrapper (AjaxResponse -> IO [SomeStoreAction])
+    deriving Typeable
+
+-- | This is broken out as a separate function because if the code is inlined (either manually or by
+-- ghc), there is a runtime error.
+useHandler :: AjaxResponse -> HandlerWrapper -> IO ()
+useHandler r (HandlerWrapper h) = do
+    actions <- h r
+    actions `deepseq` mapM_ executeAction actions
+{-# NOINLINE useHandler #-} -- if this is inlined, there is a bug in ghcjs
+#endif
+
+-- | If you are going to use 'ajax' or 'jsonAjax', you must call 'initAjax' once from your main
+-- function.  The call should appear before the call to 'reactRender'.
+initAjax :: IO ()
+#ifdef __GHCJS__
+initAjax = do
+    return ()
+    a <- asyncCallback2 $ \rawXhr h -> do
+        resp <- if js_reqTimedOut h
+                  then return $ AjaxResponse 504 "Timeout" rawXhr
+                  else AjaxResponse <$> js_xhrStatus rawXhr <*> js_xhrResponseText rawXhr <*> pure rawXhr
+        e <- js_getHandlerWrapper h
+        js_release e
+        parseExport e >>= useHandler resp
+
+    js_setAjaxCallback a
+#else
+initAjax = return ()
+#endif
+
+-- | Use @XMLHttpRequest@ to send a request to the backend.  Once the response arrives
+-- and the @readyState@ is done, the response will be passed to the given handler and the resulting
+-- actions will be executed.  Note that 'ajax' returns immedietly and does not wait for the request
+-- to finish.
+ajax :: AjaxRequest -> (AjaxResponse -> IO [SomeStoreAction]) -> IO ()
+#ifdef __GHCJS__
+ajax req handler = do
+    reqJson <- toJSVal req
+    handlerE <- export $ HandlerWrapper handler
+    js_ajax reqJson handlerE
+#else
+ajax _ _ = return ()
+#endif
+
+-- | Use @XMLHttpRequest@ to send a request with a JSON body, parse the response body as JSON, and
+-- then dispatch some actions with the response.  This should be used from within the transform
+-- function of your store.  For example,
+--
+-- >data Target = AlienShip | AlienPlanet
+-- >  deriving (Show, Typeable, Generic, ToJSON, FromJSON)
+-- >
+-- >data Trajectory = Trajectory
+-- >    { x :: Double, y :: Double, z :: Double, vx :: Double, vy :: Double, vz :: Double }
+-- >  deriving (Show, Typeable, Generic, ToJSON, FromJSON)
+-- >
+-- >data UpdatePending = NoUpdatePending | UpdatePending Text | PreviousUpdateHadError Text
+-- >
+-- >data MyStore = MyStore { currentTrajectory :: Maybe Trajectory, launchUpdate :: UpdatePending }
+-- >
+-- >data MyStoreAction = LaunchTheMissiles Target
+-- >                   | MissilesLaunched Trajectory
+-- >                   | UnableToLaunchMissiles Text
+-- >  deriving (Typeable, Generic, NFData)
+-- >
+-- >instance StoreData MyStore where
+-- >    type StoreAction MyStore = MyStoreAction
+-- >
+-- >    transform (LaunchTheMissiles t) s = do
+-- >        jsonAjax NoTimeout "PUT" "/launch-the-missiles" [] t $ \case
+-- >            Left (_, msg) -> return [SomeStoreAction myStore $ UnableToLaunchMissiles msg]
+-- >            Right traj -> return [SomeStoreAction myStore $ MissilesLaunched traj]
+-- >        return s { launchUpdate = UpdatePending ("Requesting missle launch against " ++ T.pack (show t)) }
+-- >
+-- >    transform (MissilesLaunched traj) s =
+-- >        return s { currentTrajectory = Just traj, launchUpdate = NoUpdatePending }
+-- >
+-- >    transform (UnableToLaunchMissiles err) s =
+-- >        return s { launchUpdate = PreviousUpdateHadError err }
+-- >
+-- >myStore :: ReactStore MyStore
+-- >myStore = mkStore $ MyStore Nothing NoUpdatePending
+--
+-- And then in your view, you can render this using something like:
+--
+-- >myView :: ReactView ()
+-- >myView = defineControllerView "launch the missles" myStore $ \s () -> do
+-- >    case launchUpdate s of
+-- >        NoUpdatePending -> return ()
+-- >        UpdatePending msg -> span_ $ faIcon_ "rocket" <> elemText msg
+-- >        PreviousUpdateHadErroer err -> span_ $ faIcon_ "exclamation" <> elemText err
+-- >    clbutton_ ["pure-button button-success"] ([SomeStoreAction myStore $ LaunchTheMissiles AlienShip]) $ do
+-- >        faIcon_ "rocket"
+-- >        "Launch the missles against the alien ship!"
+-- >    p_ $ elemString $ "Current trajectory " ++ show (currentTrajectory s)
+--
+jsonAjax :: (ToJSON body, FromJSON response)
+         => RequestTimeout
+         -> Text -- ^ the method
+         -> Text -- ^ the URI
+         -> [(Text, Text)] -- ^ the headers.  In addition to these headers, 'jsonAjax' adds two headers:
+                           -- @Content-Type: application/json@ and @Accept: application/json@.
+         -> body -- ^ the body
+         -> (Either (Int, Text) response -> IO [SomeStoreAction])
+            -- ^ Once @XMLHttpRequest@ changes the @readyState@ to done this handler will be
+            -- executed and the resulting actions dispatched to the stores.
+            --
+            --   * If the response status is @200@, the body will be parsed as JSON and a
+            --     'Right' value will be passed to this handler.   If there is an
+            --     error parsing the JSON response, a 'Left' value with @500@ and the error message
+            --     from aeson is given to the handler. 
+            --
+            --   * If the response status is anything besides @200@, a 'Left' value with a pair
+            --     of the response status and response text is passed to the handler.
+         -> IO ()
+#ifdef __GHCJS__
+jsonAjax timeout method uri headers body handler = do
+    bodyRef <- toJSVal_aeson body >>= js_JSONstringify
+    let extraHeaders = [("Content-Type", "application/json"), ("Accept", "application/json")]
+    let req = AjaxRequest
+              { reqMethod = JSS.textToJSString method
+              , reqURI = JSS.textToJSString uri
+              , reqHeaders = extraHeaders ++ map (JSS.textToJSString *** JSS.textToJSString) headers
+              , reqTimeout = timeout
+              , reqBody = bodyRef
+              }
+    ajax req $ \resp ->
+        if respStatus resp == 200
+            then do
+                j <- js_JSONParse $ respResponseText resp
+                mv <- fromJSVal j
+                case mv of
+                    Nothing -> handler $ Left (500, "Unable to convert response body")
+                    Just v -> case fromJSON v of
+                        Success v' -> handler $ Right v'
+                        Error e -> handler $ Left (500, T.pack e)
+            else handler $ Left (respStatus resp, JSS.textFromJSString $ respResponseText resp)
+#else
+jsonAjax _ _ _ _ _ = return ()
+#endif
+
+#ifdef __GHCJS__
+foreign import javascript unsafe
+    "hsreact$ajax($1, $2)"
+    js_ajax :: JSVal -> Export HandlerWrapper -> IO ()
+
+foreign import javascript unsafe
+    "hsreact$setAjaxCallback($1)"
+    js_setAjaxCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO ()
+
+foreign import javascript unsafe
+    "JSON['parse']($1)"
+    js_JSONParse :: JSString -> IO JSVal
+
+foreign import javascript unsafe
+    "JSON['stringify']($1)"
+    js_JSONstringify :: JSVal -> IO JSVal
+
+foreign import javascript unsafe
+    "$1.hs"
+    js_getHandlerWrapper :: JSVal -> IO (Export HandlerWrapper)
+
+foreign import javascript unsafe
+    "$r = $1.timedOut"
+    js_reqTimedOut :: JSVal -> Bool
+
+foreign import javascript unsafe
+    "$1['status']"
+    js_xhrStatus :: JSVal -> IO Int
+
+foreign import javascript unsafe
+    "$1['responseText']"
+    js_xhrResponseText :: JSVal -> IO JSString
+
+foreign import javascript unsafe
+    "h$release($1)"
+    js_release :: Export HandlerWrapper -> IO ()
+
+instance ToJSVal AjaxRequest
+#endif
diff --git a/src/React/Flux/Combinators.hs b/src/React/Flux/Combinators.hs
--- a/src/React/Flux/Combinators.hs
+++ b/src/React/Flux/Combinators.hs
@@ -9,36 +9,17 @@
   , foreign_
   , labeledInput_
   , style
-  -- * Ajax
-  , initAjax
-  , jsonAjax
-  , AjaxRequest(..)
-  , AjaxResponse(..)
-  , ajax
 ) where
 
-import Data.Aeson
 import Data.Monoid ((<>))
-import Data.Text (Text)
-import Data.Typeable (Typeable)
-import GHC.Generics (Generic)
 import React.Flux.DOM
 import React.Flux.Internal
 import React.Flux.PropertiesAndEvents
-import React.Flux.Store
 import React.Flux.Views
 
 #ifdef __GHCJS__
-import Control.Arrow ((***))
-import Control.DeepSeq (deepseq)
-import GHCJS.Foreign.Callback
-import GHCJS.Marshal (ToJSVal(..), toJSVal_aeson, FromJSVal(..))
 import GHCJS.Types (JSVal)
-import qualified Data.Text as T
-import qualified Data.JSString.Text as JSS
 
-import React.Flux.Export
-
 foreign import javascript unsafe
     "$r = window[$1]"
     js_lookupWindow :: JSString -> JSVal
@@ -132,198 +113,3 @@
 -- on elements.
 style :: [(JSString,JSString)] -> PropertyOrHandler handler
 style = nestedProperty "style" . map (\(n,a) -> (n &= a))
-
---------------------------------------------------------------------------------
---- Ajax
---------------------------------------------------------------------------------
-
--- | The input to an AJAX request built using @XMLHttpRequest@.
-data AjaxRequest = AjaxRequest
-  { reqMethod :: JSString
-  , reqURI :: JSString
-  , reqHeaders :: [(JSString, JSString)]
-  , reqBody :: JSVal
-  } deriving (Typeable, Generic)
-
--- | The response after @XMLHttpRequest@ indicates that the @readyState@ is done.
-data AjaxResponse = AjaxResponse
-  { respStatus :: Int
-  , respResponseText :: JSString
-  , respResponseXHR :: JSVal -- ^ The raw @XMLHttpRequest@ object.  Use this if you want more status about the response,
-                             --   such as response headers.
-  } deriving (Typeable, Generic)
-
-#ifdef __GHCJS__
--- | GHCJS panics if we export the function directly, so wrap it in a data struct
-data HandlerWrapper = HandlerWrapper (AjaxResponse -> IO [SomeStoreAction])
-    deriving Typeable
-
--- | This is broken out as a separate function because if the code is inlined (either manually or by
--- ghc), there is a runtime error.
-useHandler :: AjaxResponse -> HandlerWrapper -> IO ()
-useHandler r (HandlerWrapper h) = do
-    actions <- h r
-    actions `deepseq` mapM_ executeAction actions
-{-# NOINLINE useHandler #-} -- if this is inlined, there is a bug in ghcjs
-#endif
-
--- | If you are going to use 'ajax' or 'jsonAjax', you must call 'initAjax' once from your main
--- function.  The call should appear before the call to 'reactRender'.
-initAjax :: IO ()
-#ifdef __GHCJS__
-initAjax = do
-    return ()
-    a <- asyncCallback2 $ \rawXhr h -> do
-        resp <- AjaxResponse <$> js_xhrStatus rawXhr <*> js_xhrResponseText rawXhr <*> pure rawXhr
-        e <- js_getHandlerWrapper h
-        js_release e
-        parseExport e >>= useHandler resp
-
-    js_setAjaxCallback a
-#else
-initAjax = return ()
-#endif
-
--- | Use @XMLHttpRequest@ to send a request to the backend.  Once the response arrives
--- and the @readyState@ is done, the response will be passed to the given handler and the resulting
--- actions will be executed.  Note that 'ajax' returns immedietly and does not wait for the request
--- to finish.
-ajax :: AjaxRequest -> (AjaxResponse -> IO [SomeStoreAction]) -> IO ()
-#ifdef __GHCJS__
-ajax req handler = do
-    reqJson <- toJSVal req
-    handlerE <- export $ HandlerWrapper handler
-    js_ajax reqJson handlerE
-#else
-ajax _ _ = return ()
-#endif
-
--- | Use @XMLHttpRequest@ to send a request with a JSON body, parse the response body as JSON, and
--- then dispatch some actions with the response.  This should be used from within the transform
--- function of your store.  For example,
---
--- >data Target = AlienShip | AlienPlanet
--- >  deriving (Show, Typeable, Generic, ToJSON, FromJSON)
--- >
--- >data Trajectory = Trajectory
--- >    { x :: Double, y :: Double, z :: Double, vx :: Double, vy :: Double, vz :: Double }
--- >  deriving (Show, Typeable, Generic, ToJSON, FromJSON)
--- >
--- >data UpdatePending = NoUpdatePending | UpdatePending Text | PreviousUpdateHadError Text
--- >
--- >data MyStore = MyStore { currentTrajectory :: Maybe Trajectory, launchUpdate :: UpdatePending }
--- >
--- >data MyStoreAction = LaunchTheMissiles Target
--- >                   | MissilesLaunched Trajectory
--- >                   | UnableToLaunchMissiles Text
--- >  deriving (Typeable, Generic, NFData)
--- >
--- >instance StoreData MyStore where
--- >    type StoreAction MyStore = MyStoreAction
--- >
--- >    transform (LaunchTheMissiles t) s = do
--- >        jsonAjax "PUT" "/launch-the-missiles" [] t $ \case
--- >            Left (_, msg) -> return [SomeStoreAction myStore $ UnableToLaunchMissiles msg]
--- >            Right traj -> return [SomeStoreAction myStore $ MissilesLaunched traj]
--- >        return s { launchUpdate = UpdatePending ("Requesting missle launch against " ++ T.pack (show t)) }
--- >
--- >    transform (MissilesLaunched traj) s =
--- >        return s { currentTrajectory = Just traj, launchUpdate = NoUpdatePending }
--- >
--- >    transform (UnableToLaunchMissiles err) s =
--- >        return s { launchUpdate = PreviousUpdateHadError err }
--- >
--- >myStore :: ReactStore MyStore
--- >myStore = mkStore $ MyStore Nothing NoUpdatePending
---
--- And then in your view, you can render this using something like:
--- 
--- >myView :: ReactView ()
--- >myView = defineControllerView "launch the missles" myStore $ \s () -> do
--- >    case launchUpdate s of
--- >        NoUpdatePending -> return ()
--- >        UpdatePending msg -> span_ $ faIcon_ "rocket" <> elemText (T.unpack msg)
--- >        PreviousUpdateHadErroer err -> span_ $ faIcon_ "exclamation" <> elemText (T.unpack err)
--- >    clbutton_ ["pure-button button-success"] ([SomeStoreAction myStore $ LaunchTheMissiles AlienShip]) $ do
--- >        faIcon_ "rocket"
--- >        "Launch the missles against the alien ship!"
--- >    p_ $ elemText $ "Current trajectory " ++ show (currentTrajectory s)
---
-jsonAjax :: (ToJSON body, FromJSON response)
-         => Text -- ^ the method
-         -> Text -- ^ the URI
-         -> [(Text, Text)] -- ^ the headers.  In addition to these headers, 'jsonAjax' adds two headers:
-                           -- @Content-Type: application/json@ and @Accept: application/json@.
-         -> body -- ^ the body
-         -> (Either (Int, Text) response -> IO [SomeStoreAction])
-            -- ^ Once @XMLHttpRequest@ changes the @readyState@ to done this handler will be
-            -- executed and the resulting actions dispatched to the stores.
-            --
-            --   * If the response status is @200@, the body will be parsed as JSON and a
-            --     'Right' value will be passed to this handler.   If there is an
-            --     error parsing the JSON response, a 'Left' value with @500@ and the error message
-            --     from aeson is given to the handler. 
-            --
-            --   * If the response status is anything besides @200@, a 'Left' value with a pair
-            --     of the response status and response text is passed to the handler.
-         -> IO ()
-#ifdef __GHCJS__
-jsonAjax method uri headers body handler = do
-    bodyRef <- toJSVal_aeson body >>= js_JSONstringify
-    let extraHeaders = [("Content-Type", "application/json"), ("Accept", "application/json")]
-    let req = AjaxRequest
-              { reqMethod = JSS.textToJSString method
-              , reqURI = JSS.textToJSString uri
-              , reqHeaders = extraHeaders ++ map (JSS.textToJSString *** JSS.textToJSString) headers
-              , reqBody = bodyRef
-              }
-    ajax req $ \resp ->
-        if respStatus resp == 200
-            then do
-                j <- js_JSONParse $ respResponseText resp
-                mv <- fromJSVal j
-                case mv of
-                    Nothing -> handler $ Left (500, "Unable to convert response body")
-                    Just v -> case fromJSON v of
-                        Success v' -> handler $ Right v'
-                        Error e -> handler $ Left (500, T.pack e)
-            else handler $ Left (respStatus resp, JSS.textFromJSString $ respResponseText resp)
-#else
-jsonAjax _ _ _ _ _ = return ()
-#endif
-
-#ifdef __GHCJS__
-foreign import javascript unsafe
-    "hsreact$ajax($1, $2)"
-    js_ajax :: JSVal -> Export HandlerWrapper -> IO ()
-
-foreign import javascript unsafe
-    "hsreact$setAjaxCallback($1)"
-    js_setAjaxCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO ()
-
-foreign import javascript unsafe
-    "JSON['parse']($1)"
-    js_JSONParse :: JSString -> IO JSVal
-
-foreign import javascript unsafe
-    "JSON['stringify']($1)"
-    js_JSONstringify :: JSVal -> IO JSVal
-
-foreign import javascript unsafe
-    "$1.hs"
-    js_getHandlerWrapper :: JSVal -> IO (Export HandlerWrapper)
-
-foreign import javascript unsafe
-    "$1['status']"
-    js_xhrStatus :: JSVal -> IO Int
-
-foreign import javascript unsafe
-    "$1['responseText']"
-    js_xhrResponseText :: JSVal -> IO JSString
-
-foreign import javascript unsafe
-    "h$release($1)"
-    js_release :: Export HandlerWrapper -> IO ()
-
-instance ToJSVal AjaxRequest
-#endif
diff --git a/test/client/test-client.html b/test/client/test-client.html
--- a/test/client/test-client.html
+++ b/test/client/test-client.html
@@ -4,8 +4,8 @@
     </head>
     <body>
         <div id="app"></div>
-        <script src="https://fb.me/react-with-addons-15.0.1.js"></script>
-        <script src="https://fb.me/react-dom-15.0.1.js"></script>
+        <script src="https://fb.me/react-with-addons-15.3.0.js"></script>
+        <script src="https://fb.me/react-dom-15.3.0.js"></script>
         <script src="node_modules/react-intl/dist/react-intl.js"></script>
         <script src="node_modules/react-intl/locale-data/en.js"></script>
         <script src="../../js-build/install-root/bin/test-client.jsexe/all.js"></script>
