diff --git a/ghc-src/Miso/Html/Internal.hs b/ghc-src/Miso/Html/Internal.hs
--- a/ghc-src/Miso/Html/Internal.hs
+++ b/ghc-src/Miso/Html/Internal.hs
@@ -49,15 +49,15 @@
   , onBeforeDestroyed
   ) where
 
-import           Data.Aeson (Value(..), ToJSON(..))
+import           Data.Aeson  (Value(..), ToJSON(..))
 import qualified Data.Map    as M
-import           Data.Monoid
 import           Data.Proxy
 import           Data.String (IsString(..))
 import qualified Data.Text   as T
 import qualified Data.Vector as V
 import qualified Lucid       as L
 import qualified Lucid.Base  as L
+import           Prelude     hiding (null)
 import           Servant.API
 
 import           Miso.Event
@@ -187,7 +187,12 @@
 -- | `VNode` creation
 node :: NS -> MisoString -> Maybe Key -> [Attribute action] -> [View action] -> View action
 node vNs vType vKey as xs =
-  let vProps  = Props  $ M.fromList [ (k,v) | P k v <- as ]
+  let classes = intercalate " " [ v | P "class" (String v) <- as ]
+      vProps  = Props $ do
+        let propClass = M.fromList [ (k,v) | P k v <- as ]
+        if not (null classes)
+          then M.insert "class" (String classes) propClass
+          else propClass
       vChildren = V.fromList $ map runView xs
   in View VNode {..}
 
diff --git a/ghcjs-ffi/Miso/FFI.hs b/ghcjs-ffi/Miso/FFI.hs
--- a/ghcjs-ffi/Miso/FFI.hs
+++ b/ghcjs-ffi/Miso/FFI.hs
@@ -95,7 +95,12 @@
 
 -- | Set property on object
 set :: ToJSVal v => JSString -> v -> OI.Object -> IO ()
+set "class" v obj = toJSVal v >>= appendClass obj
 set k v obj = toJSVal v >>= \x -> OI.setProp k x obj
+
+-- | Only used for 'class', guaranteed to be a MisoString
+foreign import javascript unsafe "if ('class' in $1) { $1['class'] += ' ' + $2; } else { $1['class'] = $2; }"
+  appendClass :: OI.Object -> JSVal -> IO ()
 
 foreign import javascript unsafe "$1.addEventListener($2, $3);"
   addEventListener' :: JSVal -> JSString -> Callback (JSVal -> IO ()) -> IO ()
diff --git a/jsaddle-ffi/Miso/FFI.hs b/jsaddle-ffi/Miso/FFI.hs
--- a/jsaddle-ffi/Miso/FFI.hs
+++ b/jsaddle-ffi/Miso/FFI.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ViewPatterns #-}
 {-# LANGUAGE LambdaCase #-}
 -----------------------------------------------------------------------------
 -- |
@@ -80,6 +81,17 @@
 
 -- | Set property on object
 set :: ToJSVal v => JSString -> v -> OI.Object -> JSM ()
+set (unpack -> "class") v obj = do
+  classSet <- ((pack "class") `elem`) <$> listProps obj
+  if classSet
+    then do
+      classStr <- fromJSValUnchecked =<< getProp (pack "class") obj
+      vStr <- fromJSValUnchecked =<< toJSVal v
+      v' <- toJSVal (classStr <> pack " " <> vStr)
+      setProp (pack "class") v' obj
+    else do
+      v' <- toJSVal v
+      setProp (pack "class") v' obj
 set k v obj = do
   v' <- toJSVal v
   setProp k v' obj
diff --git a/jsbits/isomorphic.js b/jsbits/isomorphic.js
--- a/jsbits/isomorphic.js
+++ b/jsbits/isomorphic.js
@@ -25,10 +25,14 @@
     } else if (mountPoint.childNodes.length === 0) {
 	node = mountPoint.appendChild (doc.createElement('div'));
     } else {
-	while (mountPoint.childNodes[mountChildIdx].localName === 'script' || mountPoint.childNodes[mountChildIdx].nodeType === Node.TEXT_NODE){
+	while (mountPoint.childNodes[mountChildIdx] && (mountPoint.childNodes[mountChildIdx].nodeType === Node.TEXT_NODE || mountPoint.childNodes[mountChildIdx].localName === 'script')){
 	  mountChildIdx++;
 	}
-	node = mountPoint.childNodes[mountChildIdx];
+	if (!mountPoint.childNodes[mountChildIdx]) {
+	    node = doc.body.appendChild (doc.createElement('div'));
+	} else {
+	    node = mountPoint.childNodes[mountChildIdx];
+        }
     }
 
     if (!window['walk'](logLevel,vtree, node, doc)) {
diff --git a/miso.cabal b/miso.cabal
--- a/miso.cabal
+++ b/miso.cabal
@@ -1,5 +1,5 @@
 name:                miso
-version:             1.7.0.0
+version:             1.7.1.0
 category:            Web, Miso, Data Structures
 license:             BSD3
 license-file:        LICENSE
diff --git a/src/Miso/Html/Element.hs b/src/Miso/Html/Element.hs
--- a/src/Miso/Html/Element.hs
+++ b/src/Miso/Html/Element.hs
@@ -489,24 +489,28 @@
 link_ :: [Attribute action] -> View action
 link_ = flip (nodeHtml "link") []
 -- | https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style
+--
 -- This takes the raw text to be put in the style tag.
+--
 -- That means that if any part of the text is not trusted there's
 -- a potential CSS injection. Read more at
 -- https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client_Side_Testing/05-Testing_for_CSS_Injection
 --
 -- You can also easily shoot yourself in the foot with something like:
 --
---   style_ [] "</style>"
+-- @'style_' [] "\</style\>"@
 style_ :: [Attribute action] -> MisoString -> View action
 style_ attrs rawText = node HTML "style" Nothing attrs [textRaw rawText]
 -- | https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
+--
 -- This takes the raw text to be put in the script tag.
+--
 -- That means that if any part of the text is not trusted there's
 -- a potential JavaScript injection. Read more at
 -- https://owasp.org/www-community/attacks/xss/
 --
 -- You can also easily shoot yourself in the foot with something like:
 --
---   script_ [] "</script>"
+-- @'script_' [] "\</script\>"@
 script_ :: [Attribute action] -> MisoString -> View action
 script_ attrs rawText = node HTML "script" Nothing attrs [textRaw rawText]
