diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Changelog
 
+## 0.1.0.2
+
+* Bump to Datastar RC.8
+* Add end-to-end testing with Playwright
+
 ## 0.1.0.1
 
 * Add example apps: hello-world (warp/servant/channel), activity-feed, heap-view
diff --git a/datastar-hs.cabal b/datastar-hs.cabal
--- a/datastar-hs.cabal
+++ b/datastar-hs.cabal
@@ -1,6 +1,6 @@
 cabal-version:   3.0
 name:            datastar-hs
-version:         0.1.0.1
+version:         0.1.0.2
 synopsis:        Haskell bindings for Datastar
 description:
   Server-side SDK for building real-time hypermedia applications with
@@ -43,6 +43,30 @@
     , http-types >= 0.12  && < 1
     , text       >= 1.2   && < 3
     , wai        >= 3.2   && < 4
+
+executable e2e-server
+  hs-source-dirs:   e2e-server
+  main-is:          Main.hs
+  build-depends:
+    , base
+    , aeson
+    , bytestring
+    , datastar-hs
+    , http-types
+    , lucid2
+    , text
+    , warp
+    , wai
+  default-language: Haskell2010
+  default-extensions:
+    DeriveGeneric
+    DeriveAnyClass
+    DerivingStrategies
+    ImportQualifiedPost
+    OverloadedStrings
+    ScopedTypeVariables
+  ghc-options: -threaded
+
 
 test-suite datastar-hs-test
   type:             exitcode-stdio-1.0
diff --git a/e2e-server/Main.hs b/e2e-server/Main.hs
new file mode 100644
--- /dev/null
+++ b/e2e-server/Main.hs
@@ -0,0 +1,83 @@
+module Main where
+
+import Data.Aeson (FromJSON)
+import Data.ByteString.Lazy qualified as LBS
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Hypermedia.Datastar
+import Lucid
+import Lucid.Base (makeAttributes)
+import Network.HTTP.Types (status200, status404)
+import Network.Wai qualified as WAI
+import Network.Wai.Handler.Warp qualified as Warp
+
+newtype Greeting = Greeting {greeting :: T.Text}
+  deriving (Generic)
+  deriving anyclass (FromJSON)
+
+app :: WAI.Request -> (WAI.Response -> IO b) -> IO b
+app req respond = case (WAI.requestMethod req, WAI.pathInfo req) of
+  ("GET", []) ->
+    respond $ WAI.responseLBS status200 [("Content-Type", "text/html")] testPage
+  ("GET", ["sse", "patch-elements"]) ->
+    respond $ sseResponse nullLogger $ \gen ->
+      sendPatchElements gen (patchElements "<div id=\"pe-result\">Patched Content</div>")
+  ("GET", ["sse", "patch-signals"]) ->
+    respond $ sseResponse nullLogger $ \gen ->
+      sendPatchSignals gen (patchSignals "{\"message\":\"Signal Updated\"}")
+  ("GET", ["sse", "execute-script"]) ->
+    respond $ sseResponse nullLogger $ \gen ->
+      sendExecuteScript gen (executeScript "document.getElementById('es-result').textContent = 'Script Executed'")
+  ("GET", ["sse", "read-signals"]) ->
+    respond $ sseResponse nullLogger $ \gen -> do
+      result <- readSignals req
+      case result of
+        Right (signals :: Greeting) ->
+          sendPatchElements gen (patchElements ("<div id=\"rs-result\">" <> greeting signals <> "</div>"))
+        Left err ->
+          sendPatchElements gen (patchElements ("<div id=\"rs-result\">Error: " <> T.pack err <> "</div>"))
+  ("GET", ["sse", "multiple-events"]) ->
+    respond $ sseResponse nullLogger $ \gen -> do
+      sendPatchElements gen (patchElements "<div id=\"me-result\">Event 1</div>")
+      sendPatchElements gen (patchElements "<div id=\"me-result\">Event 2</div>")
+      sendPatchElements gen (patchElements "<div id=\"me-result\">Event 3</div>")
+  _ ->
+    respond $ WAI.responseLBS status404 [] "Not found"
+
+testPage :: LBS.ByteString
+testPage = renderBS $ doctypehtml_ $ do
+  head_ $ do
+    title_ "datastar-hs e2e tests"
+    script_ [type_ "module", src_ "https://cdn.jsdelivr.net/gh/starfederation/datastar@1.0.0-RC.8/bundles/datastar.js"] ("" :: T.Text)
+  body_ [makeAttributes "data-signals" "{}"] $ do
+    h1_ "datastar-hs E2E Test Page"
+
+    section_ [id_ "patch-elements-test"] $ do
+      h2_ "Patch Elements"
+      button_ [id_ "pe-trigger", makeAttributes "data-on:click" "@get('/sse/patch-elements')"] "Trigger"
+      div_ [id_ "pe-result"] "Waiting..."
+
+    section_ [id_ "patch-signals-test", makeAttributes "data-signals:message" "'Waiting...'"] $ do
+      h2_ "Patch Signals"
+      button_ [id_ "ps-trigger", makeAttributes "data-on:click" "@get('/sse/patch-signals')"] "Trigger"
+      div_ [id_ "ps-result", makeAttributes "data-text" "$message"] "Waiting..."
+
+    section_ [id_ "execute-script-test"] $ do
+      h2_ "Execute Script"
+      button_ [id_ "es-trigger", makeAttributes "data-on:click" "@get('/sse/execute-script')"] "Trigger"
+      div_ [id_ "es-result"] "Waiting..."
+
+    section_ [id_ "read-signals-test", makeAttributes "data-signals:greeting" "'Hello from Browser'"] $ do
+      h2_ "Read Signals"
+      button_ [id_ "rs-trigger", makeAttributes "data-on:click" "@get('/sse/read-signals')"] "Trigger"
+      div_ [id_ "rs-result"] "Waiting..."
+
+    section_ [id_ "multiple-events-test"] $ do
+      h2_ "Multiple Events"
+      button_ [id_ "me-trigger", makeAttributes "data-on:click" "@get('/sse/multiple-events')"] "Trigger"
+      div_ [id_ "me-result"] "Waiting..."
+
+main :: IO ()
+main = do
+  putStrLn "e2e-server running on http://localhost:3113"
+  Warp.run 3113 app
diff --git a/src/Hypermedia/Datastar.hs b/src/Hypermedia/Datastar.hs
--- a/src/Hypermedia/Datastar.hs
+++ b/src/Hypermedia/Datastar.hs
@@ -76,13 +76,14 @@
   , isDatastarRequest
 
     -- * Logger
-  , DatastarLogger(..)
+  , DatastarLogger (..)
   , nullLogger
   , stderrLogger
   )
 where
 
 import Hypermedia.Datastar.ExecuteScript (ExecuteScript (..), executeScript)
+import Hypermedia.Datastar.Logger (DatastarLogger, nullLogger, stderrLogger)
 import Hypermedia.Datastar.PatchElements (PatchElements (..), patchElements, removeElements)
 import Hypermedia.Datastar.PatchSignals (PatchSignals (..), patchSignals)
 import Hypermedia.Datastar.Types (ElementNamespace (..), ElementPatchMode (..), EventType (..))
@@ -95,4 +96,3 @@
   , sendPatchSignals
   , sseResponse
   )
-import Hypermedia.Datastar.Logger (DatastarLogger, nullLogger, stderrLogger)
diff --git a/src/Hypermedia/Datastar/WAI.hs b/src/Hypermedia/Datastar/WAI.hs
--- a/src/Hypermedia/Datastar/WAI.hs
+++ b/src/Hypermedia/Datastar/WAI.hs
@@ -55,10 +55,9 @@
 import Hypermedia.Datastar.Types
 
 import Hypermedia.Datastar.ExecuteScript qualified as ES
+import Hypermedia.Datastar.Logger qualified as Logger
 import Hypermedia.Datastar.PatchElements qualified as PE
 import Hypermedia.Datastar.PatchSignals qualified as PS
-import Hypermedia.Datastar.Logger qualified as Logger
-import qualified Hypermedia.Datastar.Logger as Logger
 
 {- | An opaque handle for sending SSE events to the browser.
 
@@ -71,7 +70,7 @@
   { sseWrite :: BSB.Builder -> IO ()
   , sseFlush :: IO ()
   , sseLock :: MVar ()
-  , sseLogger :: Logger.DatastarLogger 
+  , sseLogger :: Logger.DatastarLogger
   }
 
 {- | Create a WAI 'WAI.Response' that streams SSE events.
diff --git a/test/Hypermedia/Datastar/SSESpec.hs b/test/Hypermedia/Datastar/SSESpec.hs
--- a/test/Hypermedia/Datastar/SSESpec.hs
+++ b/test/Hypermedia/Datastar/SSESpec.hs
@@ -9,11 +9,11 @@
 import Data.List (isInfixOf)
 
 import Hypermedia.Datastar
+import Hypermedia.Datastar.Logger (nullLogger)
 import Hypermedia.Datastar.PatchElements
 import Hypermedia.Datastar.PatchSignals
 import Hypermedia.Datastar.Types
 import Hypermedia.Datastar.WAI (renderEvent)
-import Hypermedia.Datastar.Logger (nullLogger)
 import Network.Wai.Internal (Response (..))
 
 render :: DatastarEvent -> String
