diff --git a/client_src/css/main.css b/client_src/css/main.css
index 16895e0..9885961 100644
--- a/client_src/css/main.css
+++ b/client_src/css/main.css
@@ -43,3 +43,7 @@ main {
     top: initial !important; bottom: 0;
     z-index: 100 !important;
 }
+
+.device-property-list {
+    margin: 0; padding: 0 0 0 1em;
+}
diff --git a/client_src/js/LayerDeviceMarkers.mjs b/client_src/js/LayerDeviceMarkers.mjs
index 8a62452..7818414 100644
--- a/client_src/js/LayerDeviceMarkers.mjs
+++ b/client_src/js/LayerDeviceMarkers.mjs
@@ -2,6 +2,9 @@
 
 import L from 'leaflet';
 import 'leaflet.markercluster';
+// import CreateElement from 'dom-create-element-query-selector';
+// We're using the git repo for now until an update is released, and rollup doesn't like that apparently
+import CreateElement from '../../node_modules/dom-create-element-query-selector/src/index.js';
 
 import Config from './Config.mjs';
 import GetFromUrl from './Helpers/GetFromUrl.mjs';
@@ -31,6 +34,7 @@ class LayerDeviceMarkers {
 	}
 	
 	add_device_marker(device) {
+		// Create the marker
 		let marker = L.marker(
 			L.latLng(device.latitude, device.longitude),
 			{ // See https://leafletjs.com/reference-1.4.0.html#marker
@@ -39,8 +43,51 @@ class LayerDeviceMarkers {
 				autoPanPadding: L.point(100, 100)
 			}
 		);
+		// Create the popup
+		let popup = L.popup({
+			className: "popup-device",
+			autoPanPadding: L.point(100, 100)
+		}).setContent("&#x231b; Loading..."); // TODO: Display a nice loading animation here
+		marker.on("popupopen", this.marker_popup_open_handler.bind(this, device.id));
+		
+		marker.bindPopup(popup);
+		
 		this.layer.addLayer(marker);
 	}
+	
+	async marker_popup_open_handler(device_id, event) {
+		if(typeof device_id !== "number")
+			throw new Exception("Error: Invalid device id passed.");
+		
+		let device_info = JSON.parse(await GetFromUrl(`${Config.api_root}?action=device-info&device-id=${device_id}`));
+		
+		event.popup.setContent(this.render_device_info(device_info));
+	}
+	
+	render_device_info(device_info) {
+		let result = document.createDocumentFragment();
+		
+		result.appendChild(CreateElement("h2.device-name",
+			`Device: ${device_info.name}`
+		));
+		result.querySelector(".device-name").dataset.id = device_info.id;
+		
+		
+		let info_list = [];
+		for(let property in device_info) {
+			// Filter out properties we're handling specially
+			if(["id"].includes(property)) continue;
+			
+			info_list.push(CreateElement(
+				"li.device-property",
+				`${property.split("_").map((word) => word[0].toUpperCase()+word.slice(1)).join(" ")}: ${device_info[property]}`
+			));
+		}
+		result.appendChild(CreateElement("ul.device-property-list", ...info_list));
+		
+		
+		return result;
+	}
 }
 
 export default LayerDeviceMarkers;
diff --git a/client_src/js/UI.mjs b/client_src/js/UI.mjs
index db6832d..196135b 100644
--- a/client_src/js/UI.mjs
+++ b/client_src/js/UI.mjs
@@ -25,7 +25,7 @@ class UI {
 				}).bind(this)
 			}
 		]);
-		this.ui_panel.watch((event) => console.log(event));
+		// this.ui_panel.watch((event) => console.log(event));
 	}
 }
 
diff --git a/package-lock.json b/package-lock.json
index 022f18d..6d470d6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -649,6 +649,10 @@
       "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
       "dev": true
     },
+    "dom-create-element-query-selector": {
+      "version": "github:hekigan/dom-create-element-query-selector#39afc3fb24cc06d280ef81e167acf3d21513e3a2",
+      "from": "github:hekigan/dom-create-element-query-selector"
+    },
     "electron-to-chromium": {
       "version": "1.3.103",
       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.103.tgz",
diff --git a/package.json b/package.json
index 3f1d407..defb183 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
   },
   "homepage": "https://github.com/sbrl/ConnectedHumber-Air-Quality-Interface#readme",
   "dependencies": {
+    "dom-create-element-query-selector": "github:hekigan/dom-create-element-query-selector",
     "leaflet": "^1.4.0",
     "leaflet-fullscreen": "^1.0.2",
     "leaflet-timedimension": "^1.1.0",
