diff --git a/client/src/components/AttachRoomPanel.svelte b/client/src/components/AttachRoomPanel.svelte
index da319f8ebaa3f178b61ef34e8c61ded25665281c..5fa46d641a68f7202759741e4e04b23e6c4702a3 100644
--- a/client/src/components/AttachRoomPanel.svelte
+++ b/client/src/components/AttachRoomPanel.svelte
@@ -4,16 +4,36 @@
   
   import { API_BASE_URL } from '../stores.js';
   export let roomUrl = "";
+  $: trimmedUrlInput = roomUrl.trim();
+  $: isValidBBBUrl = trimmedUrlInput.length > 0 && isValidUrl(trimmedUrlInput);
+
+  let createRoomRequestInProgress = false;
+
+  function isValidUrl(inputString) {
+    try {
+      const _ = new URL(inputString);
+      return true;
+    } catch (error) {
+      return (error instanceof TypeError) ? false : error;
+    }
+  }
 
   async function requestAttachToRoom() {
-    await fetch(API_BASE_URL+'/attach', {
-      method: 'POST',
-      headers: {
-        'Content-Type': 'application/json'
-      },
-      body: JSON.stringify({url: roomUrl})
-    })
+    createRoomRequestInProgress = true;
+    try {
+      await fetch(API_BASE_URL+'/attach', {
+        method: 'POST',
+        headers: {
+         'Content-Type': 'application/json'
+       },
+        body: JSON.stringify({url: trimmedUrlInput})
+      })
+    } catch (error) {
+      console.error("Could not send request to attach to room", trimmedUrlInput);
+      console.error(error);
+    }
     roomUrl = '';
+    createRoomRequestInProgress = false;
   }
 </script>
 
@@ -28,7 +48,7 @@
     <br />
   </div>
   <div class="panel-block">
-    <button class="button is-success is-outlined is-fullwidth" on:click={requestAttachToRoom}>
+    <button type="submit" class:is-loading={createRoomRequestInProgress} class="button is-success is-outlined is-fullwidth" disabled={!isValidBBBUrl} on:click={requestAttachToRoom}>
       Attach
     </button>
   </div>
diff --git a/client/src/components/ChatBroadcastPanel.svelte b/client/src/components/ChatBroadcastPanel.svelte
index ee9069188dee3676556f875f1d4f9090b89e0788..44e900c4b226e2ace57f43c9cf97cc4a2c3b4e78 100644
--- a/client/src/components/ChatBroadcastPanel.svelte
+++ b/client/src/components/ChatBroadcastPanel.svelte
@@ -1,6 +1,7 @@
 <script>
   import { API_BASE_URL } from '../stores.js';
   let content = "";
+  $: hasContent = content.trim().length !== 0;
 
   async function sendBroadcast() {
     try {
@@ -34,8 +35,8 @@
     <br />
   </div>
   <div class="panel-block">
-    <button class="button is-link is-outlined is-fullwidth" on:click={sendBroadcast}>
-      Send to all attached rooms
+    <button type="submit" class="button is-link is-outlined is-fullwidth" disabled={!hasContent} on:click={sendBroadcast}>
+      Send to attached rooms
     </button>
   </div>
 </nav>