diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,23 @@
 # Revision history for reflex-dom-core
 
+## Unreleased
+
+## 0.5.2
+
+* Update to use new dependent-sum/map packages and drop dependency on `*Tag` classes (e.g., `ShowTag`).
+* Update version bounds of base, containers, and stm
+* Update to use the newly split `these`/`semialign` packages. To use the pre-split `these` package, set the `split-these` flag to false.
+* Reintroduce "data-ssr": elements without this attribute are skipped during
+  hydration.
+* Fix an issue in the hydration tests that prevented the test from finding the chromium executable
+* Relax constraints on `dyn` and `widgetHold` to match the ones in `networkView` and `networkHold` respectively
+* Fix prerender for RequesterT so that it doesn't accidentally discard a request that is made at the same moment as getPostBuild's Event fires
+
+## 0.5.1
+
+* Added support for marking elements with a "data-skip-hydration" attribute, which will cause hydration to ignore and skip over them.
+* Removed "data-ssr" attributes from statically rendered output.
+
 ## 0.5
 
 * Add HydrationDomBuilderT to support hydration of statically rendered DOM nodes. See the note at the top of Reflex.Dom.Builder.Immediate.
diff --git a/reflex-dom-core.cabal b/reflex-dom-core.cabal
--- a/reflex-dom-core.cabal
+++ b/reflex-dom-core.cabal
@@ -1,5 +1,5 @@
 Name: reflex-dom-core
-Version: 0.5
+Version: 0.5.2.0
 Synopsis: Functional Reactive Web Apps with Reflex
 Description: Reflex-DOM is a Functional Reactive web framework based on the Reflex FRP engine
 License: BSD3
@@ -39,22 +39,27 @@
   default: False
   manual: True
 
+flag split-these
+  description: Use split these/semialign packages
+  manual:      False
+  default:     True
+
 library
   hs-source-dirs: src
   build-depends:
-    aeson >= 0.8 && < 1.4,
-    base >= 4.7 && < 4.12,
+    aeson >= 0.8 && < 1.5,
+    base >= 4.7 && < 4.13,
     bifunctors >= 4.2 && < 6,
     bimap >= 0.3 && < 0.4,
     blaze-builder,
     bytestring == 0.10.*,
-    containers == 0.5.*,
-    constraints >= 0.9 && < 0.11,
-    contravariant >= 1.4 && < 1.5,
+    containers >= 0.6 && < 0.7,
+    constraints >= 0.9 && < 0.12,
+    contravariant >= 1.4 && < 1.6,
     data-default >= 0.5 && < 0.8,
-    dependent-map == 0.2.*,
-    dependent-sum >= 0.3 && < 0.5,
-    dependent-sum-template >= 0.0.0.4 && < 0.1,
+    dependent-map >= 0.3 && < 0.4,
+    dependent-sum >= 0.6 && < 0.7,
+    dependent-sum-template >= 0.1 && < 0.2,
     directory >= 1.2 && < 1.4,
     exception-transformers == 0.4.*,
     ghcjs-dom >= 0.9.1.0 && < 0.10,
@@ -67,11 +72,10 @@
     primitive >= 0.5 && < 0.7,
     random,
     ref-tf == 0.4.*,
-    reflex == 0.6.*,
+    reflex >= 0.6.2,
     semigroups >= 0.16 && < 0.19,
-    stm == 2.4.*,
+    stm >= 2.4 && < 2.6,
     text == 1.2.*,
-    these >= 0.4 && < 0.8,
     transformers >= 0.3 && < 0.6,
     network-uri >= 2.6.1 && < 2.7,
     zenc == 0.1.*
@@ -86,6 +90,14 @@
     if !os(windows)
       build-depends: unix == 2.7.*
 
+  if flag(split-these)
+    build-depends:
+      semialign >= 1 && < 1.1,
+      these >= 1 && < 1.1
+  else
+    build-depends:
+      these >= 0.4 && < 0.9
+
   exposed-modules:
     Foreign.JavaScript.TH
     Foreign.JavaScript.Orphans
@@ -94,6 +106,7 @@
     Reflex.Dom.Builder.Class.Events
     Reflex.Dom.Builder.Immediate
     Reflex.Dom.Builder.InputDisabled
+    Reflex.Dom.Builder.Hydratable
     Reflex.Dom.Builder.Static
     Reflex.Dom.Class
     Reflex.Dom.Core
@@ -133,8 +146,8 @@
 
   if flag(use-template-haskell)
     build-depends:
-      dependent-sum >= 0.3 && < 0.5,
-      dependent-sum-template >= 0.0.0.5 && < 0.1,
+      dependent-sum >= 0.6,
+      dependent-sum-template >= 0.1 && < 0.2,
       template-haskell
     other-extensions: TemplateHaskell
     cpp-options: -DUSE_TEMPLATE_HASKELL
@@ -142,7 +155,7 @@
       Reflex.Dom.Builder.Class.TH
   else
     build-depends:
-      dependent-sum == 0.4.*
+      dependent-sum == 0.6.*
 
 test-suite hlint
   build-depends: base, hlint
@@ -166,6 +179,7 @@
                , filepath
                , ghcjs-dom
                , hspec
+               , hspec-core
                , hspec-webdriver
                , http-types
                , HUnit
@@ -187,6 +201,7 @@
                , warp
                , webdriver
                , websockets
+               , which
   hs-source-dirs: test
   ghc-options: -rtsopts -with-rtsopts=-T -Wall -fwarn-tabs -funbox-strict-fields -O2 -ferror-spans
   ghc-prof-options: -fprof-auto -optP-DPROFILING
diff --git a/src/Foreign/JavaScript/Orphans.hs b/src/Foreign/JavaScript/Orphans.hs
--- a/src/Foreign/JavaScript/Orphans.hs
+++ b/src/Foreign/JavaScript/Orphans.hs
@@ -1,6 +1,8 @@
 {-# OPTIONS_HADDOCK hide #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE UndecidableInstances #-}
+
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Foreign.JavaScript.Orphans where
 
diff --git a/src/Foreign/JavaScript/TH.hs b/src/Foreign/JavaScript/TH.hs
--- a/src/Foreign/JavaScript/TH.hs
+++ b/src/Foreign/JavaScript/TH.hs
@@ -6,11 +6,9 @@
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE StandaloneDeriving #-}
 {-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeSynonymInstances #-}
 {-# LANGUAGE UndecidableInstances #-}
 #ifdef USE_TEMPLATE_HASKELL
 {-# LANGUAGE TemplateHaskell #-}
@@ -294,7 +292,7 @@
 #ifdef ghcjs_HOST_OS
 
 data JSCtx_IO
-type HasJS' = HasJS JSCtx_IO
+type JS' = JSCtx_IO
 
 instance MonadIO m => HasJS JSCtx_IO (WithJSContextSingleton x m) where
   type JSX (WithJSContextSingleton x m) = IO
@@ -345,7 +343,7 @@
 #else
 
 data JSCtx_JavaScriptCore x
-type HasJS' = HasJS (JSCtx_JavaScriptCore ())
+type JS' = JSCtx_JavaScriptCore ()
 
 instance IsJSContext (JSCtx_JavaScriptCore x) where
   newtype JSRef (JSCtx_JavaScriptCore x) = JSRef_JavaScriptCore { unJSRef_JavaScriptCore :: JSVal }
diff --git a/src/Reflex/Dom/Builder/Class.hs b/src/Reflex/Dom/Builder/Class.hs
--- a/src/Reflex/Dom/Builder/Class.hs
+++ b/src/Reflex/Dom/Builder/Class.hs
@@ -11,10 +11,9 @@
 {-# LANGUAGE ImpredicativeTypes #-}
 #endif
 {-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE PolyKinds #-}
-{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE RecursiveDo #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE StandaloneDeriving #-}
diff --git a/src/Reflex/Dom/Builder/Class/Events.hs b/src/Reflex/Dom/Builder/Class/Events.hs
--- a/src/Reflex/Dom/Builder/Class/Events.hs
+++ b/src/Reflex/Dom/Builder/Class/Events.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE GADTs #-}
-{-# LANGUAGE KindSignatures #-}
 {-# LANGUAGE PolyKinds #-}
 #ifdef USE_TEMPLATE_HASKELL
 {-# LANGUAGE TemplateHaskell #-}
diff --git a/src/Reflex/Dom/Builder/Hydratable.hs b/src/Reflex/Dom/Builder/Hydratable.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Dom/Builder/Hydratable.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
+module Reflex.Dom.Builder.Hydratable where
+
+import Control.Monad.Fix
+import Control.Monad.Primitive
+import Control.Monad.Ref
+import Control.Monad.Trans
+import Control.Monad.Trans.Control
+import Data.Coerce
+import qualified Data.Map as Map
+import Foreign.JavaScript.TH
+#ifndef ghcjs_HOST_OS
+import GHCJS.DOM.Types (MonadJSM (..))
+#endif
+import Reflex
+import Reflex.Dom.Builder.Class
+import Reflex.Dom.Builder.Immediate (HasDocument (..))
+import Reflex.Host.Class
+
+-- | A DomBuilder transformer that adds "data-ssr" to all elements such that the
+-- hydration builder knows which bits of DOM were added by us, and which were
+-- added by external scripts.
+newtype HydratableT m a = HydratableT { runHydratableT :: m a } deriving (Functor, Applicative, Monad, MonadAtomicRef, MonadFix, MonadIO)
+
+#ifndef ghcjs_HOST_OS
+instance MonadJSM m => MonadJSM (HydratableT m) where
+    liftJSM' = HydratableT . liftJSM'
+#endif
+
+deriving instance MonadSample t m => MonadSample t (HydratableT m)
+deriving instance MonadHold t m => MonadHold t (HydratableT m)
+
+instance MonadTrans HydratableT where
+  lift = HydratableT
+
+instance MonadTransControl HydratableT where
+  type StT HydratableT a = a
+  liftWith f = HydratableT $ f runHydratableT
+  restoreT = HydratableT
+
+instance MonadRef m => MonadRef (HydratableT m) where
+  type Ref (HydratableT m) = Ref m
+  newRef = lift . newRef
+  readRef = lift . readRef
+  writeRef ref = lift . writeRef ref
+
+instance PerformEvent t m => PerformEvent t (HydratableT m) where
+  type Performable (HydratableT m) = Performable m
+  performEvent_ = lift . performEvent_
+  performEvent = lift . performEvent
+
+instance PrimMonad m => PrimMonad (HydratableT m) where
+  type PrimState (HydratableT m) = PrimState m
+  primitive = lift . primitive
+
+makeHydratable :: Reflex t => ElementConfig er t m -> ElementConfig er t m
+makeHydratable cfg = cfg
+  { _elementConfig_initialAttributes = Map.insert "data-ssr" "" $ _elementConfig_initialAttributes cfg
+  , _elementConfig_modifyAttributes = fmap (Map.delete "data-ssr") <$> _elementConfig_modifyAttributes cfg
+  }
+
+instance PostBuild t m => PostBuild t (HydratableT m) where
+  getPostBuild = lift getPostBuild
+
+deriving instance TriggerEvent t m => TriggerEvent t (HydratableT m)
+
+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (HydratableT m) where
+  newEventWithTrigger = lift . newEventWithTrigger
+  newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
+
+instance Adjustable t m => Adjustable t (HydratableT m) where
+  runWithReplace a0 a' = HydratableT $ runWithReplace (coerce a0) (coerceEvent a')
+  traverseDMapWithKeyWithAdjust f dm0 dm' = HydratableT $ traverseDMapWithKeyWithAdjust (\k v -> runHydratableT $ f k v) (coerce dm0) (coerceEvent dm')
+  traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = HydratableT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runHydratableT $ f k v) (coerce dm0) (coerceEvent dm')
+  traverseIntMapWithKeyWithAdjust f m0 m' = HydratableT $ traverseIntMapWithKeyWithAdjust (\k v -> runHydratableT $ f k v) (coerce m0) (coerceEvent m')
+
+instance NotReady t m => NotReady t (HydratableT m) where
+  notReadyUntil = lift . notReadyUntil
+  notReady = lift notReady
+
+instance DomBuilder t m => DomBuilder t (HydratableT m) where
+  type DomBuilderSpace (HydratableT m) = DomBuilderSpace m
+  element t cfg = lift . element t (makeHydratable cfg) . runHydratableT
+  inputElement cfg = lift $ inputElement $ cfg
+    { _inputElementConfig_elementConfig = makeHydratable $ _inputElementConfig_elementConfig cfg
+    }
+  textAreaElement cfg = lift $ textAreaElement $ cfg
+    { _textAreaElementConfig_elementConfig = makeHydratable $ _textAreaElementConfig_elementConfig cfg
+    }
+  selectElement cfg child = do
+    let cfg' = cfg
+          { _selectElementConfig_elementConfig = makeHydratable $ _selectElementConfig_elementConfig cfg
+          }
+    lift $ selectElement cfg' $ runHydratableT child
+
+instance HasDocument m => HasDocument (HydratableT m)
+
+instance HasJSContext m => HasJSContext (HydratableT m) where
+  type JSContextPhantom (HydratableT m) = JSContextPhantom m
+  askJSContext = lift askJSContext
+
+instance HasJS js m => HasJS js (HydratableT m) where
+  type JSX (HydratableT m) = JSX m
+  liftJS = lift . liftJS
+
+instance DomRenderHook t m => DomRenderHook t (HydratableT m) where
+  withRenderHook f = HydratableT . withRenderHook f . runHydratableT
+  requestDomAction = HydratableT . requestDomAction
+  requestDomAction_ = HydratableT . requestDomAction_
diff --git a/src/Reflex/Dom/Builder/Immediate.hs b/src/Reflex/Dom/Builder/Immediate.hs
--- a/src/Reflex/Dom/Builder/Immediate.hs
+++ b/src/Reflex/Dom/Builder/Immediate.hs
@@ -347,7 +347,7 @@
   flip runTriggerEventT events $ do
     rec (result, req) <- runRequesterT a rsp
         rsp <- performEventAsync $ ffor req $ \rm f -> liftJSM $ runInAnimationFrame f $
-          traverseRequesterData (\r -> Identity <$> r) rm
+          traverseRequesterData (fmap Identity) rm
     return result
   where
     runInAnimationFrame f x = void . DOM.inAnimationFrame' $ \_ -> do
@@ -362,7 +362,7 @@
   withRenderHook hook (DomRenderHookT a) = do
     DomRenderHookT $ withRequesting $ \rsp -> do
       (x, req) <- lift $ runRequesterT a $ runIdentity <$> rsp
-      return (ffor req $ \rm -> hook $ traverseRequesterData (\r -> Identity <$> r) rm, x)
+      return (ffor req $ \rm -> hook $ traverseRequesterData (fmap Identity) rm, x)
   requestDomAction = DomRenderHookT . requestingIdentity
   requestDomAction_ = DomRenderHookT . requesting_
 
@@ -764,11 +764,18 @@
         }
   result <- HydrationDomBuilderT $ lift $ runReaderT (unHydrationDomBuilderT child) env'
   wrapResult <- liftIO newEmptyMVar
-  let ssrAttr = "data-ssr" :: DOM.JSString
-      hasSSRAttribute :: DOM.Element -> HydrationRunnerT t m Bool
-      hasSSRAttribute e = case cfg ^. namespace of
-        Nothing -> hasAttribute e ssrAttr <* removeAttribute e ssrAttr
-        Just ns -> hasAttributeNS e (Just ns) ssrAttr <* removeAttributeNS e (Just ns) ssrAttr
+  let skipAttr = "data-hydration-skip" :: DOM.JSString
+      ssrAttr = "data-ssr" :: DOM.JSString
+      shouldSkip :: DOM.Element -> HydrationRunnerT t m Bool
+      shouldSkip e = case cfg ^. namespace of
+        Nothing -> do
+          skip <- hasAttribute e skipAttr
+          ssr <- hasAttribute e ssrAttr
+          pure $ skip || not ssr
+        Just ns -> do
+          skip <- hasAttributeNS e (Just ns) skipAttr
+          ssr <- hasAttributeNS e (Just ns) ssrAttr
+          pure $ skip || not ssr
   childDom <- liftIO $ readIORef childDelayedRef
   let rawCfg = extractRawElementConfig cfg
   doc <- askDocument
@@ -783,9 +790,9 @@
             pure e
           Just node -> DOM.castTo DOM.Element node >>= \case
             Nothing -> go (Just node) -- this node is not an element, skip
-            Just e -> hasSSRAttribute e >>= \case
-              False -> go (Just node) -- this element was not added by the static renderer, skip
-              True -> do
+            Just e -> shouldSkip e >>= \case
+              True -> go (Just node) -- this element is explicitly marked for being skipped by hydration
+              False -> do
                 t <- Element.getTagName e
                 -- TODO: check attributes?
                 if T.toCaseFold elementTag == T.toCaseFold t
@@ -1555,13 +1562,13 @@
                   readIORef (_traverseChildImmediate_childReadyState immediate) >>= \case
                     ChildReadyState_Ready -> return PatchDMapWithMove.From_Delete
                     ChildReadyState_Unready _ -> do
-                      writeIORef (_traverseChildImmediate_childReadyState immediate) $ ChildReadyState_Unready $ Just $ This k
+                      writeIORef (_traverseChildImmediate_childReadyState immediate) $ ChildReadyState_Unready $ Just $ Some k
                       return $ PatchDMapWithMove.From_Insert $ Constant (_traverseChildImmediate_childReadyState immediate)
                 PatchDMapWithMove.From_Delete -> return PatchDMapWithMove.From_Delete
                 PatchDMapWithMove.From_Move fromKey -> return $ PatchDMapWithMove.From_Move fromKey
               deleteOrMove :: forall a. k a -> Product (Constant (IORef (ChildReadyState (Some k)))) (ComposeMaybe k) a -> IO (Constant () a)
               deleteOrMove _ (Pair (Constant sRef) (ComposeMaybe mToKey)) = do
-                writeIORef sRef $ ChildReadyState_Unready $ This <$> mToKey -- This will be Nothing if deleting, and Just if moving, so it works out in both cases
+                writeIORef sRef $ ChildReadyState_Unready $ Some <$> mToKey -- This will be Nothing if deleting, and Just if moving, so it works out in both cases
                 return $ Constant ()
           p' <- fmap unsafePatchDMapWithMove $ DMap.traverseWithKey new $ unPatchDMapWithMove p
           _ <- DMap.traverseWithKey deleteOrMove $ PatchDMapWithMove.getDeletionsAndMoves p old
@@ -1571,8 +1578,8 @@
       phsBefore <- liftIO $ readIORef placeholders
       let collectIfMoved :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose (TraverseChild t m (Some k)) v') a -> JSM (Constant (Maybe DOM.DocumentFragment) a)
           collectIfMoved k e = do
-            let mThisPlaceholder = Map.lookup (This k) phsBefore -- Will be Nothing if this element wasn't present before
-                nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (This k) phsBefore
+            let mThisPlaceholder = Map.lookup (Some k) phsBefore -- Will be Nothing if this element wasn't present before
+                nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some k) phsBefore
             case isJust $ getComposeMaybe $ PatchDMapWithMove._nodeInfo_to e of
               False -> do
                 mapM_ (`deleteUpTo` nextPlaceholder) mThisPlaceholder
@@ -1591,7 +1598,7 @@
             PatchMapWithMove.From_Move k -> Just $ PatchMapWithMove.From_Move k
       let placeFragment :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Compose (TraverseChild t m (Some k)) v') a -> JSM (Constant () a)
           placeFragment k e = do
-            let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (This k) phsAfter
+            let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some k) phsAfter
             case PatchDMapWithMove._nodeInfo_from e of
               PatchDMapWithMove.From_Insert (Compose (TraverseChild x _)) -> case x of
                 Left _ -> pure ()
@@ -1622,7 +1629,7 @@
                 readIORef (_traverseChildImmediate_childReadyState immediate) >>= \case
                   ChildReadyState_Ready -> return Nothing -- Delete this child, since it's ready
                   ChildReadyState_Unready _ -> do
-                    writeIORef (_traverseChildImmediate_childReadyState immediate) $ ChildReadyState_Unready $ Just $ This k
+                    writeIORef (_traverseChildImmediate_childReadyState immediate) $ ChildReadyState_Unready $ Just $ Some k
                     return $ Just $ Constant (_traverseChildImmediate_childReadyState immediate)
             delete _ (Constant sRef) = do
               writeIORef sRef $ ChildReadyState_Unready Nothing
@@ -1633,9 +1640,9 @@
   hoistTraverseWithKeyWithAdjust traverseDMapWithKeyWithAdjust mapPatchDMap updateChildUnreadiness $ \placeholders lastPlaceholder (PatchDMap patch) -> do
     phs <- liftIO $ readIORef placeholders
     forM_ (DMap.toList patch) $ \(k :=> ComposeMaybe mv) -> do
-      let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (This k) phs
+      let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some k) phs
       -- Delete old node
-      forM_ (Map.lookup (This k) phs) $ \thisPlaceholder -> do
+      forM_ (Map.lookup (Some k) phs) $ \thisPlaceholder -> do
         thisPlaceholder `deleteUpTo` nextPlaceholder
       -- Insert new node
       forM_ mv $ \(Compose (TraverseChild e _)) -> case e of
@@ -1785,7 +1792,7 @@
             liftIO $ writeIORef childReadyState ChildReadyState_Ready
             case countedAt of
               Nothing -> return ()
-              Just (This k) -> do -- This child has been counted as unready, so we need to remove it from the unready set
+              Just (Some k) -> do -- This child has been counted as unready, so we need to remove it from the unready set
                 (oldUnready, p) <- liftIO $ readIORef pendingChange
                 when (not $ DMap.null oldUnready) $ do -- This shouldn't actually ever be null
                   let newUnready = DMap.delete k oldUnready
@@ -1800,7 +1807,7 @@
           readIORef (_traverseChildImmediate_childReadyState immediate) >>= \case
             ChildReadyState_Ready -> return Nothing
             ChildReadyState_Unready _ -> do
-              writeIORef (_traverseChildImmediate_childReadyState immediate) $ ChildReadyState_Unready $ Just $ This k
+              writeIORef (_traverseChildImmediate_childReadyState immediate) $ ChildReadyState_Unready $ Just $ Some k
               return $ Just $ Constant (_traverseChildImmediate_childReadyState immediate)
   initialUnready <- liftIO $ DMap.mapMaybeWithKey (\_ -> getComposeMaybe) <$> DMap.traverseWithKey processChild children0
   liftIO $ if DMap.null initialUnready
diff --git a/src/Reflex/Dom/Builder/InputDisabled.hs b/src/Reflex/Dom/Builder/InputDisabled.hs
--- a/src/Reflex/Dom/Builder/InputDisabled.hs
+++ b/src/Reflex/Dom/Builder/InputDisabled.hs
@@ -80,6 +80,7 @@
   runWithReplace a0 a' = InputDisabledT $ runWithReplace (coerce a0) (coerceEvent a')
   traverseDMapWithKeyWithAdjust f dm0 dm' = InputDisabledT $ traverseDMapWithKeyWithAdjust (\k v -> runInputDisabledT $ f k v) (coerce dm0) (coerceEvent dm')
   traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = InputDisabledT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runInputDisabledT $ f k v) (coerce dm0) (coerceEvent dm')
+  traverseIntMapWithKeyWithAdjust f m0 m' = InputDisabledT $ traverseIntMapWithKeyWithAdjust (\k v -> runInputDisabledT $ f k v) (coerce m0) (coerceEvent m')
 
 instance NotReady t m => NotReady t (InputDisabledT m) where
   notReadyUntil = lift . notReadyUntil
@@ -108,3 +109,8 @@
 instance HasJS js m => HasJS js (InputDisabledT m) where
   type JSX (InputDisabledT m) = JSX m
   liftJS = lift . liftJS
+
+instance DomRenderHook t m => DomRenderHook t (InputDisabledT m) where
+  withRenderHook f = InputDisabledT . withRenderHook f . runInputDisabledT
+  requestDomAction = InputDisabledT . requestDomAction
+  requestDomAction_ = InputDisabledT . requestDomAction_
diff --git a/src/Reflex/Dom/Builder/Static.hs b/src/Reflex/Dom/Builder/Static.hs
--- a/src/Reflex/Dom/Builder/Static.hs
+++ b/src/Reflex/Dom/Builder/Static.hs
@@ -288,7 +288,7 @@
     StaticDomBuilderT $ do
       let shouldEscape = elementTag `Set.notMember` noEscapeElements
       (result, innerHtml) <- lift $ lift $ runStaticDomBuilderT child $ StaticDomBuilderEnv shouldEscape Nothing
-      attrs0 <- foldDyn applyMap (Map.insert "data-ssr" "" $ cfg ^. initialAttributes) (cfg ^. modifyAttributes)
+      attrs0 <- foldDyn applyMap (cfg ^. initialAttributes) (cfg ^. modifyAttributes)
       selectValue <- asks _staticDomBuilderEnv_selectValue
       let addSelectedAttr attrs sel = case Map.lookup "value" attrs of
             Just v | v == sel -> attrs <> Map.singleton "selected" ""
diff --git a/src/Reflex/Dom/Core.hs b/src/Reflex/Dom/Core.hs
--- a/src/Reflex/Dom/Core.hs
+++ b/src/Reflex/Dom/Core.hs
@@ -6,6 +6,7 @@
 
 import Reflex as X hiding (askEvents)
 import Reflex.Dom.Builder.Class as X
+import Reflex.Dom.Builder.Hydratable as X
 import Reflex.Dom.Builder.Immediate as X
 import Reflex.Dom.Builder.InputDisabled as X
 import Reflex.Dom.Builder.Static as X
diff --git a/src/Reflex/Dom/Main.hs b/src/Reflex/Dom/Main.hs
--- a/src/Reflex/Dom/Main.hs
+++ b/src/Reflex/Dom/Main.hs
@@ -121,8 +121,8 @@
 {-# INLINABLE runHydrationWidgetWithHeadAndBody #-}
 runHydrationWidgetWithHeadAndBody
   :: JSM ()
-  -> (   (forall c. HydrationWidget () c -> FloatingWidget () c) -- "Append to head"
-      -> (forall c. HydrationWidget () c -> FloatingWidget () c) -- "Append to body"
+  -> (   (forall c. HydrationWidget () c -> FloatingWidget () c) -- "Append to head" --TODO: test invoking this more than once
+      -> (forall c. HydrationWidget () c -> FloatingWidget () c) -- "Append to body" --TODO: test invoking this more than once
       -> FloatingWidget () ()
      )
   -> JSM ()
@@ -212,39 +212,48 @@
 attachWidget :: DOM.IsElement e => e -> JSContextSingleton x -> Widget x a -> JSM a
 attachWidget rootElement wv w = fst <$> attachWidget' rootElement wv w
 
--- | Warning: `mainWidgetWithHead'` is provided only as performance tweak. It is expected to disappear in future releases.
-mainWidgetWithHead' :: (a -> Widget () b, b -> Widget () a) -> JSM ()
-mainWidgetWithHead' widgets = withJSContextSingletonMono $ \jsSing -> do
-  doc <- currentDocumentUnchecked
-  headElement <- getHeadUnchecked doc
-  headFragment <- createDocumentFragment doc
-  bodyElement <- getBodyUnchecked doc
-  bodyFragment <- createDocumentFragment doc
-  hydrationMode <- liftIO $ newIORef HydrationMode_Immediate
-  (events, fc) <- liftIO . attachWidget'' $ \events -> do
-    let (headWidget, bodyWidget) = widgets
+{-# INLINABLE runImmediateWidgetWithHeadAndBody #-}
+runImmediateWidgetWithHeadAndBody
+  :: (   (forall c. Widget () c -> FloatingWidget () c) -- "Append to head"
+      -> (forall c. Widget () c -> FloatingWidget () c) -- "Append to body"
+      -> FloatingWidget () ()
+     )
+  -> JSM ()
+runImmediateWidgetWithHeadAndBody app = withJSContextSingletonMono $ \jsSing -> do
+  globalDoc <- currentDocumentUnchecked
+  headElement <- getHeadUnchecked globalDoc
+  bodyElement <- getBodyUnchecked globalDoc
+  headFragment <- createDocumentFragment globalDoc
+  bodyFragment <- createDocumentFragment globalDoc
+  (events, fc) <- liftIO . attachImmediateWidget $ \hydrationMode events -> do
     (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
-    let go :: forall c. Widget () c -> DOM.DocumentFragment -> PerformEventT DomTimeline DomHost c
-        go w df = do
+    let go :: forall c. DOM.DocumentFragment -> Widget () c -> FloatingWidget () c
+        go df w = do
           unreadyChildren <- liftIO $ newIORef 0
           delayed <- liftIO $ newIORef $ pure ()
           let builderEnv = HydrationDomBuilderEnv
-                { _hydrationDomBuilderEnv_document = toDocument doc
+                { _hydrationDomBuilderEnv_document = globalDoc
                 , _hydrationDomBuilderEnv_parent = Left $ toNode df
                 , _hydrationDomBuilderEnv_unreadyChildren = unreadyChildren
-                , _hydrationDomBuilderEnv_commitAction = return () --TODO
+                , _hydrationDomBuilderEnv_commitAction = pure () --TODO: possibly `replaceElementContents n f`
+                , _hydrationDomBuilderEnv_hydrationMode = hydrationMode
                 , _hydrationDomBuilderEnv_switchover = never
                 , _hydrationDomBuilderEnv_delayed = delayed
-                , _hydrationDomBuilderEnv_hydrationMode = hydrationMode
                 }
-          runWithJSContextSingleton (runPostBuildT (runHydrationDomBuilderT w builderEnv events) postBuild) jsSing
-    rec b <- go (headWidget a) headFragment
-        a <- go (bodyWidget b) bodyFragment
+          lift $ runHydrationDomBuilderT w builderEnv events
+    runWithJSContextSingleton (runPostBuildT (runTriggerEventT (app (go headFragment) (go bodyFragment)) events) postBuild) jsSing
     return (events, postBuildTriggerRef)
   replaceElementContents headElement headFragment
   replaceElementContents bodyElement bodyFragment
   liftIO $ processAsyncEvents events fc
 
+-- | Warning: `mainWidgetWithHead'` is provided only as performance tweak. It is expected to disappear in future releases.
+mainWidgetWithHead' :: (a -> Widget () b, b -> Widget () a) -> JSM ()
+mainWidgetWithHead' (h, b) = runImmediateWidgetWithHeadAndBody $ \appendHead appendBody -> do
+  rec hOut <- appendHead $ h bOut
+      bOut <- appendBody $ b hOut
+  pure ()
+
 replaceElementContents :: DOM.IsElement e => e -> DOM.DocumentFragment -> JSM ()
 replaceElementContents e df = do
   setInnerHTML e ("" :: String)
@@ -256,8 +265,7 @@
 attachWidget' rootElement jsSing w = do
   doc <- getOwnerDocumentUnchecked rootElement
   df <- createDocumentFragment doc
-  hydrationMode <- liftIO $ newIORef HydrationMode_Immediate
-  ((a, events), fc) <- liftIO . attachWidget'' $ \events -> do
+  ((a, events), fc) <- liftIO . attachImmediateWidget $ \hydrationMode events -> do
     (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
     unreadyChildren <- liftIO $ newIORef 0
     delayed <- liftIO $ newIORef $ pure ()
@@ -278,12 +286,18 @@
 
 type EventChannel = Chan [DSum (EventTriggerRef DomTimeline) TriggerInvocation]
 
-{-# INLINABLE attachWidget'' #-}
-attachWidget'' :: (EventChannel -> PerformEventT DomTimeline DomHost (a, IORef (Maybe (EventTrigger DomTimeline ())))) -> IO (a, FireCommand DomTimeline DomHost)
-attachWidget'' w = do
+{-# INLINABLE attachImmediateWidget #-}
+attachImmediateWidget
+  :: (   IORef HydrationMode
+      -> EventChannel
+      -> PerformEventT DomTimeline DomHost (a, IORef (Maybe (EventTrigger DomTimeline ())))
+     )
+  -> IO (a, FireCommand DomTimeline DomHost)
+attachImmediateWidget w = do
+  hydrationMode <- liftIO $ newIORef HydrationMode_Immediate
   events <- newChan
   runDomHost $ do
-    ((result, postBuildTriggerRef), fc@(FireCommand fire)) <- hostPerformEventT $ w events
+    ((result, postBuildTriggerRef), fc@(FireCommand fire)) <- hostPerformEventT $ w hydrationMode events
     mPostBuildTrigger <- readRef postBuildTriggerRef
     forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
     return (result, fc)
@@ -325,3 +339,8 @@
           { _appInput_window = w
           }
   return ()
+
+{-# DEPRECATED attachWidget'' "Use 'attachImmediateWidget . const' instead" #-}
+{-# INLINABLE attachWidget'' #-}
+attachWidget'' :: (EventChannel -> PerformEventT DomTimeline DomHost (a, IORef (Maybe (EventTrigger DomTimeline ())))) -> IO (a, FireCommand DomTimeline DomHost)
+attachWidget'' = attachImmediateWidget . const
diff --git a/src/Reflex/Dom/Old.hs b/src/Reflex/Dom/Old.hs
--- a/src/Reflex/Dom/Old.hs
+++ b/src/Reflex/Dom/Old.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE ExplicitForAll #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
diff --git a/src/Reflex/Dom/Prerender.hs b/src/Reflex/Dom/Prerender.hs
--- a/src/Reflex/Dom/Prerender.hs
+++ b/src/Reflex/Dom/Prerender.hs
@@ -1,17 +1,18 @@
 {-# LANGUAGE ConstraintKinds #-}
-{-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE EmptyCase #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE Rank2Types #-}
 {-# LANGUAGE RecursiveDo #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeSynonymInstances #-}
 {-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE UndecidableSuperClasses #-}
 
 -- | Render the first widget on the server, and the second on the client.
 module Reflex.Dom.Prerender
@@ -21,21 +22,22 @@
        , PrerenderBaseConstraints
        ) where
 
-import Control.Monad.Primitive (PrimMonad)
+import Control.Monad.Primitive (PrimMonad(..))
 import Control.Monad.Reader
 import Control.Monad.Ref (MonadRef(..))
 import Data.IORef (IORef, newIORef)
 import Data.Semigroup (Semigroup)
 import Data.Text (Text)
+import Data.Void
 import Foreign.JavaScript.TH
 import GHCJS.DOM.Types (MonadJSM)
 import Reflex hiding (askEvents)
 import Reflex.Dom.Builder.Class
+import Reflex.Dom.Builder.Hydratable
 import Reflex.Dom.Builder.Immediate
 import Reflex.Dom.Builder.InputDisabled
 import Reflex.Dom.Builder.Static
 import Reflex.Host.Class
-import Reflex.Requester.Base
 import Data.IntMap.Strict (IntMap)
 import qualified Data.IntMap.Strict as IntMap
 
@@ -49,7 +51,6 @@
   , DomRenderHook t m
   , HasDocument m
   , TriggerEvent t m
-  , Prerender js t m
   , PrerenderBaseConstraints js t m
   )
 
@@ -77,16 +78,16 @@
 -- hydration builder will run *both* widgets.
 prerender_
   :: (Functor m, Reflex t, Prerender js t m)
-  => m () -> ((PrerenderClientConstraint js t (Client m)) => Client m ()) -> m ()
+  => m () ->  Client m () -> m ()
 prerender_ server client = void $ prerender server client
 
-class Prerender js t m | m -> t js where
+class (PrerenderClientConstraint js t (Client m), Client (Client m) ~ Client m, Prerender js t (Client m)) => Prerender js t m | m -> t js where
   -- | Monad in which the client widget is built
   type Client m :: * -> *
   -- | Render the first widget on the server, and the second on the client. The
   -- hydration builder will run *both* widgets, updating the result dynamic at
   -- switchover time.
-  prerender :: m a -> ((PrerenderClientConstraint js t (Client m)) => Client m a) -> m (Dynamic t a)
+  prerender :: m a -> Client m a -> m (Dynamic t a)
 
 instance (ReflexHost t, Adjustable t m, PrerenderBaseConstraints js t m) => Prerender js t (HydrationDomBuilderT GhcjsDomSpace t m) where
   type Client (HydrationDomBuilderT GhcjsDomSpace t m) = HydrationDomBuilderT GhcjsDomSpace t m
@@ -131,10 +132,106 @@
         insertBefore df =<< deleteToPrerenderEnd doc
     holdDyn a0 a'
 
-data NoJavaScript -- This type should never have a HasJS instance
+newtype UnrunnableT js t m a = UnrunnableT (ReaderT Void m a)
+  deriving (Functor, Applicative, Monad, MonadTrans)
 
-instance (js ~ NoJavaScript, SupportsStaticDomBuilder t m) => Prerender js t (StaticDomBuilderT t m) where
-  type Client (StaticDomBuilderT t m) = HydrationDomBuilderT GhcjsDomSpace t m
+unrunnable :: UnrunnableT js t m a
+unrunnable = UnrunnableT $ ReaderT $ \case {}
+
+instance (Reflex t, Monad m) => DomBuilder t (UnrunnableT js t m) where
+  type DomBuilderSpace (UnrunnableT js t m) = GhcjsDomSpace
+  textNode _ = unrunnable
+  commentNode _ = unrunnable
+  element _ _ _ = unrunnable
+  inputElement _ = unrunnable
+  textAreaElement _ = unrunnable
+  selectElement _ _ = unrunnable
+  placeRawElement _ = unrunnable
+  wrapRawElement _ _ = unrunnable
+instance (Reflex t, Monad m) => NotReady t (UnrunnableT js t m) where
+  notReadyUntil _ = unrunnable
+  notReady = unrunnable
+instance (Reflex t, Monad m) => Adjustable t (UnrunnableT js t m) where
+  runWithReplace _ _ = unrunnable
+  traverseIntMapWithKeyWithAdjust _ _ _ = unrunnable
+  traverseDMapWithKeyWithAdjust _ _ _ = unrunnable
+  traverseDMapWithKeyWithAdjustWithMove _ _ _ = unrunnable
+instance (Reflex t, Monad m) => PerformEvent t (UnrunnableT js t m) where
+  type Performable (UnrunnableT js t m) = UnrunnableT js t m
+  performEvent _ = unrunnable
+  performEvent_ _ = unrunnable
+instance Monad m => MonadRef (UnrunnableT js t m) where
+  type Ref (UnrunnableT js t m) = Ref IO
+  newRef _ = unrunnable
+  readRef _ = unrunnable
+  writeRef _ _ = unrunnable
+instance Monad m => HasDocument (UnrunnableT js t m) where
+  askDocument = unrunnable
+instance Monad m => HasJSContext (UnrunnableT js t m) where
+  type JSContextPhantom (UnrunnableT js t m) = ()
+  askJSContext = unrunnable
+instance Monad m => HasJS JS' (UnrunnableT js t m) where
+  type JSX (UnrunnableT js t m) = UnrunnableT js t m
+  liftJS _ = unrunnable
+instance Monad m => MonadJS JS' (UnrunnableT js t m) where
+  runJS _ _ = unrunnable
+  forkJS _ = unrunnable
+  mkJSUndefined = unrunnable
+  isJSNull _ = unrunnable
+  isJSUndefined _ = unrunnable
+  fromJSBool _ = unrunnable
+  fromJSString _ = unrunnable
+  fromJSArray _ = unrunnable
+  fromJSUint8Array _ = unrunnable
+  fromJSNumber _ = unrunnable
+  withJSBool _ _ = unrunnable
+  withJSString _ _ = unrunnable
+  withJSNumber _ _ = unrunnable
+  withJSArray _ _ = unrunnable
+  withJSUint8Array _ _ = unrunnable
+  mkJSFun _ = unrunnable
+  freeJSFun _ = unrunnable
+  setJSProp _ _ _ = unrunnable
+  getJSProp _ _ = unrunnable
+  withJSNode _ _ = unrunnable
+instance Monad m => TriggerEvent t (UnrunnableT js t m) where
+  newTriggerEvent = unrunnable
+  newTriggerEventWithOnComplete = unrunnable
+  newEventWithLazyTriggerWithOnComplete _ = unrunnable
+instance Monad m => MonadReflexCreateTrigger t (UnrunnableT js t m) where
+  newEventWithTrigger _ = unrunnable
+  newFanEventWithTrigger _ = unrunnable
+instance Monad m => MonadFix (UnrunnableT js t m) where
+  mfix _ = unrunnable
+instance Monad m => MonadHold t (UnrunnableT js t m) where
+  hold _ _ = unrunnable
+  holdDyn _ _ = unrunnable
+  holdIncremental _ _ = unrunnable
+  buildDynamic _ _ = unrunnable
+  headE _ = unrunnable
+instance Monad m => MonadSample t (UnrunnableT js t m) where
+  sample _ = unrunnable
+instance Monad m => MonadIO (UnrunnableT js t m) where
+  liftIO _ = unrunnable
+#ifndef ghcjs_HOST_OS
+instance Monad m => MonadJSM (UnrunnableT js t m) where
+  liftJSM' _ = unrunnable
+#endif
+instance (Reflex t, Monad m) => PostBuild t (UnrunnableT js t m) where
+  getPostBuild = unrunnable
+instance Monad m => PrimMonad (UnrunnableT js t m) where
+  type PrimState (UnrunnableT js t m) = PrimState IO
+  primitive _ = unrunnable
+instance (Reflex t, Monad m) => DomRenderHook t (UnrunnableT js t m) where
+  withRenderHook _ _ = unrunnable
+  requestDomAction _ = unrunnable
+  requestDomAction_ _ = unrunnable
+instance (Reflex t, Monad m) => Prerender JS' t (UnrunnableT js t m) where
+  type Client (UnrunnableT js t m) = UnrunnableT js t m
+  prerender _ _ = unrunnable
+
+instance (SupportsStaticDomBuilder t m) => Prerender JS' t (StaticDomBuilderT t m) where
+  type Client (StaticDomBuilderT t m) = UnrunnableT JS' t m
   prerender server _ = do
     _ <- commentNode $ CommentNodeConfig startMarker Nothing
     a <- server
@@ -174,8 +271,8 @@
           (x, e) <- runRequesterT w (selectInt fannedResponses selector)
           pure (x, fmapCheap (IntMap.singleton selector) e)
     (result, requestsDyn) <- fmap splitDynPure $ lift $ prerender (withFannedResponses server 0) (withFannedResponses client 1)
-    responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switch $ current requestsDyn
-    pure result
+    responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requestsDyn
+    return result
 
 instance (Prerender js t m, Monad m, Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q) => Prerender js t (QueryT t q m) where
   type Client (QueryT t q m) = QueryT t q (Client m)
@@ -187,8 +284,12 @@
     pure a
 
 instance (Prerender js t m, Monad m) => Prerender js t (InputDisabledT m) where
-  type Client (InputDisabledT m) = Client m
-  prerender (InputDisabledT server) client = InputDisabledT $ prerender server client
+  type Client (InputDisabledT m) = InputDisabledT (Client m)
+  prerender (InputDisabledT server) (InputDisabledT client) = InputDisabledT $ prerender server client
+
+instance (Prerender js t m, Monad m) => Prerender js t (HydratableT m) where
+  type Client (HydratableT m) = HydratableT (Client m)
+  prerender (HydratableT server) (HydratableT client) = HydratableT $ prerender server client
 
 instance (Prerender js t m, Monad m, ReflexHost t) => Prerender js t (PostBuildT t m) where
   type Client (PostBuildT t m) = PostBuildT t (Client m)
diff --git a/src/Reflex/Dom/Widget/Basic.hs b/src/Reflex/Dom/Widget/Basic.hs
--- a/src/Reflex/Dom/Widget/Basic.hs
+++ b/src/Reflex/Dom/Widget/Basic.hs
@@ -60,6 +60,7 @@
   , partitionMapBySetLT
   ) where
 
+import Reflex.Adjustable.Class
 import Reflex.Class
 import Reflex.Collection
 import Reflex.Dom.Builder.Class
@@ -146,21 +147,21 @@
 -- | Given a Dynamic of widget-creating actions, create a widget that is recreated whenever the Dynamic updates.
 --   The returned Event of widget results occurs when the Dynamic does.
 --   Note:  Often, the type @a@ is an 'Event', in which case the return value is an Event-of-Events that would typically be flattened (via 'switchHold').
-dyn :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
+dyn :: (Adjustable t m, NotReady t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
 dyn = networkView
 
 -- | Like 'dyn' but discards result.
-dyn_ :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m ()
+dyn_ :: (Adjustable t m, NotReady t m, PostBuild t m) => Dynamic t (m a) -> m ()
 dyn_ = void . dyn
 
 -- | Given an initial widget and an Event of widget-creating actions, create a widget that is recreated whenever the Event fires.
 --   The returned Dynamic of widget results occurs when the Event does.
 --   Note:  Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened (via 'switchDyn').
-widgetHold :: (DomBuilder t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
+widgetHold :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
 widgetHold = networkHold
 
 -- | Like 'widgetHold' but discards result.
-widgetHold_ :: (DomBuilder t m, MonadHold t m) => m a -> Event t (m a) -> m ()
+widgetHold_ :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m ()
 widgetHold_ z = void . widgetHold z
 
 -- | Create a DOM element
diff --git a/src/Reflex/Dom/Widget/Lazy.hs b/src/Reflex/Dom/Widget/Lazy.hs
--- a/src/Reflex/Dom/Widget/Lazy.hs
+++ b/src/Reflex/Dom/Widget/Lazy.hs
@@ -108,7 +108,7 @@
   uniqWindow <- holdUniqDyn window
   return (uniqWindow, result)
   where
-    toStyleAttr m = "style" =: Map.foldWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m
+    toStyleAttr m = "style" =: Map.foldrWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m
     mkViewport h = toStyleAttr $ "overflow" =: "auto" <> "position" =: "absolute" <>
                                  "left" =: "0" <> "right" =: "0" <> "height" =: (T.pack (show h) <> "px")
     mkContainer h = toStyleAttr $ "position" =: "relative" <> "height" =: (T.pack (show h) <> "px")
diff --git a/src/Reflex/Dom/Widget/Resize.hs b/src/Reflex/Dom/Widget/Resize.hs
--- a/src/Reflex/Dom/Widget/Resize.hs
+++ b/src/Reflex/Dom/Widget/Resize.hs
@@ -33,6 +33,10 @@
 
 -- | A widget that wraps the given widget in a div and fires an event when resized.
 --   Adapted from @github.com\/marcj\/css-element-queries@
+--
+-- This function can cause strange scrollbars to appear in some circumstances.
+-- These can be hidden with pseudo selectors, for example, in webkit browsers:
+-- .wrapper *::-webkit-scrollbar { width: 0px; background: transparent; }
 resizeDetector :: (MonadJSM m, DomBuilder t m, PostBuild t m, TriggerEvent t m, PerformEvent t m, MonadHold t m, DomBuilderSpace m ~ GhcjsDomSpace, MonadJSM (Performable m), MonadFix m) => m a -> m (Event t (), a)
 resizeDetector = resizeDetectorWithStyle ""
 
diff --git a/src/Reflex/Dom/Xhr/Foreign.hs b/src/Reflex/Dom/Xhr/Foreign.hs
--- a/src/Reflex/Dom/Xhr/Foreign.hs
+++ b/src/Reflex/Dom/Xhr/Foreign.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE JavaScriptFFI #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeSynonymInstances #-}
 
 module Reflex.Dom.Xhr.Foreign (
     XMLHttpRequest
diff --git a/test/hlint.hs b/test/hlint.hs
--- a/test/hlint.hs
+++ b/test/hlint.hs
@@ -16,9 +16,11 @@
     , "--ignore=Use unless"
     , "--ignore=Use if"
     , "--ignore=Use fewer imports"
+    , "--ignore=Use list comprehension"
     , "--ignore=Unnecessary hiding" -- Interferes with cross-version compatibility
     , "--ignore=Use <$>"
     , "--ignore=Reduce duplication" --TODO: Re-enable this test
+    , "--ignore=Use list comprehension"
     , "--cpp-define=USE_TEMPLATE_HASKELL"
     ]
   if null ideas then exitSuccess else exitFailure
diff --git a/test/hydration.hs b/test/hydration.hs
--- a/test/hydration.hs
+++ b/test/hydration.hs
@@ -6,6 +6,7 @@
 {-# LANGUAGE ConstraintKinds #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
@@ -19,9 +20,11 @@
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 
+import Prelude hiding (fail)
 import Control.Concurrent
-import Control.Monad
+import Control.Monad hiding (fail)
 import Control.Monad.Catch
+import Control.Monad.Fail
 import Control.Monad.Fix
 import Control.Monad.IO.Class
 import Control.Monad.Ref
@@ -35,7 +38,6 @@
 import Data.GADT.Show.TH
 import Data.IORef (IORef)
 import Data.Maybe
-import Data.Monoid
 import Data.Proxy
 import Data.Text (Text)
 import Language.Javascript.JSaddle (syncPoint, liftJSM)
@@ -52,12 +54,15 @@
 import System.IO.Silently
 import System.IO.Temp
 import System.Process
+import System.Which (staticWhich)
 import qualified Test.HUnit as HUnit (assertEqual, assertFailure)
 import qualified Test.Hspec as H
+import qualified Test.Hspec.Core.Spec as H
 import Test.Hspec (xit)
 import Test.Hspec.WebDriver hiding (runWD, click, uploadFile, WD)
 import qualified Test.Hspec.WebDriver as WD
 import Test.WebDriver (WD(..))
+import Test.WebDriver.Exceptions (ServerError(..))
 
 import qualified Data.ByteString.Lazy as LBS
 import qualified Data.Dependent.Map as DMap
@@ -73,6 +78,9 @@
 import Test.Util.ChromeFlags
 import Test.Util.UnshareNetwork
 
+chromium :: FilePath
+chromium = $(staticWhich "chromium")
+
 seleniumPort, jsaddlePort :: PortNumber
 seleniumPort = 8000
 jsaddlePort = 8001
@@ -116,16 +124,7 @@
 deriveGCompare ''DKey
 deriveGShow ''DKey
 
-instance ShowTag DKey Identity where
-  showTaggedPrec = \case
-    Key_Int -> showsPrec
-    Key_Char -> showsPrec
-    Key_Bool -> showsPrec
-
-instance EqTag DKey Identity where
-  eqTagged Key_Int Key_Int = (==)
-  eqTagged Key_Char Key_Char = (==)
-  eqTagged Key_Bool Key_Bool = (==)
+deriving instance MonadFail WD
 
 main :: IO ()
 main = do
@@ -133,9 +132,9 @@
   isHeadless <- (== Nothing) <$> lookupEnv "NO_HEADLESS"
   withSandboxedChromeFlags isHeadless $ \chromeFlags -> do
     withSeleniumServer $ \selenium -> do
-      browserPath <- liftIO $ T.strip . T.pack <$> readProcess "which" [ "chromium" ] ""
+      let browserPath = T.strip $ T.pack chromium
       when (T.null browserPath) $ fail "No browser found"
-      withDebugging <- isJust <$> lookupEnv "DEBUG"
+      withDebugging <- isNothing <$> lookupEnv "NO_DEBUG"
       let wdConfig = WD.defaultConfig { WD.wdPort = fromIntegral $ _selenium_portNumber selenium }
           chromeCaps' = WD.getCaps $ chromeConfig browserPath chromeFlags
       hspec (tests withDebugging wdConfig [chromeCaps'] selenium) `finally` _selenium_stopServer selenium
@@ -280,13 +279,6 @@
         (e, ()) <- element "div" conf $ text "hello world"
         let click = domEvent Click e
         return ()
-    -- TODO check this is the correct solution
-    it "has ssr attribute, removes ssr attribute" $ runWD $ do
-      let checkSSRAttr = do
-            e <- findElemWithRetry $ WD.ByTag "div"
-            assertAttr e "data-ssr" (Just "")
-            pure e
-      testWidget' checkSSRAttr (\e -> assertAttr e "data-ssr" Nothing) $ el "div" $ text "hello world"
 
   describe "inputElement" $ do
     describe "hydration" $ session' $ do
@@ -754,9 +746,9 @@
           pb <- getPostBuild
           performEvent_ $ liftIO (putMVar lock ()) <$ pb
     it "result Dynamic is updated *after* switchover" $ runWD $ do
-      let static = checkBodyText "PostBuild"
+      let preSwitchover = checkBodyText "PostBuild"
           check = checkBodyText "Client"
-      testWidget static check $ void $ do
+      testWidget preSwitchover check $ void $ do
         d <- prerender (pure "Initial") (pure "Client")
         pb <- getPostBuild
         initial <- sample $ current d
@@ -764,16 +756,16 @@
     -- This essentially checks that the client IO runs *after* switchover/postBuild,
     -- thus can't create conflicting DOM
     it "can't exploit IO to break hydration" $ runWD $ do
-      let static = checkBodyText "Initial"
-      testWidgetStatic static $ void $ do
+      let preSwitchover = checkBodyText "Initial"
+      testWidgetStatic preSwitchover $ void $ do
         ref <- liftIO $ newRef "Initial"
         prerender_ (pure ()) (liftIO $ writeRef ref "Client")
         text <=< liftIO $ readRef ref
     -- As above, so below
     it "can't exploit triggerEvent to break hydration" $ runWD $ do
-      let static = checkBodyText "Initial"
+      let preSwitchover = checkBodyText "Initial"
           check = checkBodyText "Client"
-      testWidget static check $ void $ do
+      testWidget preSwitchover check $ void $ do
         (e, trigger) <- newTriggerEvent
         prerender_ (pure ()) (liftIO $ trigger "Client")
         textNode $ TextNodeConfig "Initial" $ Just e
@@ -812,7 +804,7 @@
           , el "span" . text <$> replace
           ]
     it "can be nested in postBuild widget" $ runWD $ do
-      replaceChan <- liftIO newChan
+      replaceChan :: Chan Text <- liftIO newChan
       let setup = findElemWithRetry $ WD.ByTag "div"
           check ssr = do
             -- Check that the original element still exists and has the correct text
@@ -962,26 +954,26 @@
         postBuildPatch = PatchDMap $ DMap.fromList [Key_Char :=> ComposeMaybe Nothing, Key_Bool :=> ComposeMaybe (Just $ Identity True)]
     it "doesn't replace elements at switchover, can delete/update/insert" $ runWD $ do
       chan <- liftIO newChan
-      let static :: WD [WD.Element]
-          static = getAndCheckInitialItems keyMap
+      let preSwitchover :: WD [WD.Element]
+          preSwitchover = getAndCheckInitialItems keyMap
           check :: [WD.Element] -> WD ()
           check xs = do
             checkInitialItems keyMap xs
             checkRemoval chan Key_Int
             checkReplace chan Key_Char 'B'
             checkInsert chan Key_Bool True
-      testWidget' static check $ do
+      testWidget' preSwitchover check $ do
         (dmap, _evt) <- traverseDMapWithKeyWithAdjust widget keyMap =<< triggerEventWithChan chan
         liftIO $ dmap `H.shouldBe` keyMap
     it "handles postBuild correctly" $ runWD $ do
       chan <- liftIO newChan
-      let static = getAndCheckInitialItems $ applyAlways postBuildPatch keyMap
+      let preSwitchover = getAndCheckInitialItems $ applyAlways postBuildPatch keyMap
           check xs = do
             withRetry $ checkInitialItems (applyAlways postBuildPatch keyMap) xs
             checkRemoval chan Key_Int
             checkInsert chan Key_Char 'B'
             checkReplace chan Key_Bool True
-      testWidget' static check $ void $ do
+      testWidget' preSwitchover check $ void $ do
         pb <- getPostBuild
         replace <- triggerEventWithChan chan
         (dmap, _evt) <- traverseDMapWithKeyWithAdjust widget keyMap $ leftmost [postBuildPatch <$ pb, replace]
@@ -1071,24 +1063,24 @@
         postBuildPatch = PatchIntMap $ IntMap.fromList [(2, Nothing), (3, Just "trois"), (4, Just "four")]
     xit "doesn't replace elements at switchover, can delete/update/insert" $ runWD $ do
       chan <- liftIO newChan
-      let static = getAndCheckInitialItems intMap
+      let preSwitchover = getAndCheckInitialItems intMap
           check xs = do
             checkInitialItems intMap xs
             checkRemoval chan 1
             checkReplace chan 2 "deux"
             checkInsert chan 4 "four"
-      testWidget' static check $ void $ do
+      testWidget' preSwitchover check $ void $ do
         (im, _evt) <- traverseIntMapWithKeyWithAdjust widget intMap =<< triggerEventWithChan chan
         liftIO $ im `H.shouldBe` intMap
     xit "handles postBuild correctly" $ runWD $ do
       chan <- liftIO newChan
-      let static = getAndCheckInitialItems $ applyAlways postBuildPatch intMap
+      let preSwitchover = getAndCheckInitialItems $ applyAlways postBuildPatch intMap
           check xs = do
             withRetry $ checkInitialItems (applyAlways postBuildPatch intMap) xs
             checkRemoval chan 1
             checkInsert chan 2 "deux"
             checkReplace chan 3 "trois"
-      testWidget' static check $ void $ do
+      testWidget' preSwitchover check $ void $ do
         pb <- getPostBuild
         replace <- triggerEventWithChan chan
         (dmap, _evt) <- traverseIntMapWithKeyWithAdjust widget intMap $ leftmost [postBuildPatch <$ pb, replace]
@@ -1153,6 +1145,9 @@
           xs <- WD.findElems (WD.ByTag "li")
           checkInitialItems dm xs
           pure xs
+        moveSpec
+          :: (DMap Key2 Identity -> (WD.Element -> Chan (PatchDMapWithMove Key2 Identity) -> WD ()) -> WD ())
+          -> H.SpecM (WdTestSession ()) ()
         moveSpec testMove = do
           it "can insert an item" $ runWD $ testMove (DMap.fromList [Key2_Int 1 ==> 1, Key2_Int 3 ==> 3]) $ \body chan -> do
             shouldContainText (T.strip $ T.unlines ["i11","i33"]) body
@@ -1180,19 +1175,19 @@
 
     describe "hydration" $ moveSpec $ \initMap test -> do
       chan <- liftIO newChan
-      let static = getAndCheckInitialItems initMap
+      let preSwitchover = getAndCheckInitialItems initMap
           check xs = do
             checkInitialItems initMap xs
             body <- getBody
             test body chan
-      testWidget' static check $ void $ do
+      testWidget' preSwitchover check $ void $ do
         (dmap, _evt) <- traverseDMapWithKeyWithAdjustWithMove widget initMap =<< triggerEventWithChan chan
         liftIO $ assertEqual "DMap" initMap dmap
 
     describe "hydration/immediate" $ moveSpec $ \initMap test -> do
       chan <- liftIO newChan
       replace <- liftIO newChan
-      lock <- liftIO newEmptyMVar
+      lock :: MVar () <- liftIO newEmptyMVar
       let check = do
             liftIO $ do
               writeChan replace ()
@@ -1211,10 +1206,12 @@
 
   describe "hydrating invalid HTML" $ session' $ do
     it "can hydrate list in paragraph" $ runWD $ do
-      let static = do
+      let preSwitchover = do
             checkBodyText "before\ninner\nafter"
             -- Two <p> tags should be present
-            [p1, p2] <- WD.findElems (WD.ByTag "p")
+            (p1, p2) <- WD.findElems (WD.ByTag "p") >>= \case
+              [p1, p2] -> pure (p1, p2)
+              _ -> error "Unexpected number of `p` tags (expected 2)"
             ol <- findElemWithRetry (WD.ByTag "ol")
             shouldContainText "before" p1
             shouldContainText "inner" ol
@@ -1225,7 +1222,7 @@
             shouldContainText "before\ninner\nafter" p1
             elementShouldBeRemoved ol
             elementShouldBeRemoved p2
-      testWidget' static check $ do
+      testWidget' preSwitchover check $ do
         -- This is deliberately invalid HTML, the browser will interpret it as
         -- <p>before</p><ol>inner</ol>after<p></p>
         el "p" $ do
@@ -1359,7 +1356,7 @@
           bodyWidget
           el "script" $ text $ TE.decodeUtf8 $ LBS.toStrict $ jsaddleJs False
   putStrLnDebug "rendering static"
-  ((), html) <- liftIO $ renderStatic staticApp
+  ((), html) <- liftIO $ renderStatic $ runHydratableT staticApp
   putStrLnDebug "rendered static"
   waitBeforeJS <- liftIO newEmptyMVar -- Empty until JS should be run
   waitUntilSwitchover <- liftIO newEmptyMVar -- Empty until switchover
@@ -1417,12 +1414,3 @@
 deriveGCompare ''Key2
 deriveGShow ''Key2
 deriveArgDict ''Key2
-
-instance ShowTag Key2 Identity where
-  showTaggedPrec = \case
-    Key2_Int _ -> showsPrec
-    Key2_Char _ -> showsPrec
-
-instance EqTag Key2 Identity where
-  eqTagged (Key2_Int _) (Key2_Int _) = (==)
-  eqTagged (Key2_Char _) (Key2_Char _) = (==)
