A modern real-time IoT monitoring platform built with React, Node.js, Socket.IO, and MQTT. Features live sensor data visualization, environmental metrics monitoring, and real-time telemetry streaming demonstrating production-grade IoT communication patterns.
π― Learning Focus: MQTT protocol, WebSocket fundamentals, IoT data streaming, real-time data visualization, buffering strategies, and React state management with high-frequency sensor data.
- React 18 + TypeScript
- Vite - Lightning-fast build tool
- TailwindCSS - Utility-first styling
- Socket.IO Client - WebSocket communication
- TanStack Router - Type-safe routing
- Recharts - Real-time data visualization
- Zod - Runtime data validation
- Node.js + Express
- Socket.IO Server - WebSocket server
- MQTT.js - MQTT client library
- TypeScript - Type safety across the stack
- MQTT Broker - Message broker (broker.hivemq for demo)
- Pub/Sub Pattern - Decoupled device communication
- QoS Levels - Reliable message delivery
- Turborepo - High-performance build system
- pnpm - Fast, disk-efficient package manager
- Shared packages - Types, ESLint, Prettier configs
- Real-time environmental sensor monitoring
- Temperature (Β°C)
- Humidity (%)
- Atmospheric Pressure (hPa)
- Light Intensity (lux)
- Status-based color coding (Normal/Warning/Critical)
- Threshold-based alerting system
- Animated status indicators for live data
- Data freshness monitoring with age warnings
- Connection quality tracking
- Responsive grid layout (mobile-first design)
- Skeleton loading states
- Mocked CPU, Memory, and Request Rate monitoring
- Interactive charts with historical data
- Automatic reconnection handling
- Start/Stop streaming controls
repo/
βββ apps/
β βββ web/ # React frontend
β β βββ src/
β β β βββ features/ # Feature-based organization
β β β β βββ sensors/ # IoT sensor dashboard
β β β β β βββ components/ # UI components
β β β β β βββ hooks/ # useSensors hook
β β β β β βββ lib/ # Utils & validation
β β β β β βββ pages/ # Dashboard page
β β β β βββ metrics/ # System metrics feature
β β β βββ routes/ # TanStack Router routes
β β β βββ shared/ # Shared hooks (useWebSocket)
β β βββ ...
β β
β βββ server/ # Node.js backend
β βββ src/
β β βββ index.ts # WebSocket & MQTT setup
β β βββ socket.ts # Socket event handlers
β β βββ services/
β β βββ mqtt-service.ts # MQTT client
β β βββ connection-manager.ts # WS connections
β β βββ metrics-generator.ts # Mocked system metrics
β βββ ...
β
βββ packages/
βββ shared/ # Shared TypeScript types
β βββ src/
β βββ sensor-types.ts # IoT data types
β βββ types.ts # WebSocket events
βββ eslint-config/ # Shared ESLint configuration
βββ prettier-config/ # Shared Prettier configuration
- Feature Colocation: Each feature contains its own components, hooks, and pages
- Shared Workspace: Common types and configurations across apps
- Clean Separation: UI components separated from WebSocket/MQTT logic
- Type Safety: End-to-end TypeScript with shared types
- SOLID Principles: Single responsibility, dependency inversion
- Data Buffering: Optimized for high-frequency IoT data streams
Used in: IoT Sensor dashboard
Why MQTT for IoT:
- β Lightweight protocol (2-byte header)
- β Pub/Sub pattern (decoupled devices)
- β QoS levels (0, 1, 2)
- β Optimized for unreliable networks
- β Low bandwidth, low power
- β Built-in reconnection
Server-Side Patterns:
- β MQTT broker connection
- β
Topic subscription (
sensors/+) - β Message publishing with QoS
- β JSON payload handling
- β Simulated sensor data generation
- β Cleanup on disconnect
Architecture Flow:
[IoT Devices/Simulator]
β MQTT (sensors/temperature)
[MQTT Broker]
β Subscribe
[Node.js Server - mqtt-service.ts]
β WebSocket
[React App - useWebSocket]
β Data buffering
[useSensors hook]
β State
[Dashboard UI]
Used in: Sensor Dashboard and Metrics
Client-Side Patterns:
- β Connection lifecycle management
- β Event subscription/unsubscription
- β Automatic reconnection with backoff
- β Data buffering (1-second throttling)
- β Runtime validation with Zod
- β Type-safe event handling
- β Memory leak prevention
Server-Side Patterns:
- β Broadcasting to subscribed clients
- β Subscription-based filtering
- β Connection tracking
- β MQTT message forwarding
- β Event handling and routing
- β Graceful shutdown
Problem: IoT sensors can publish data at high frequencies (1-10Hz), causing excessive React re-renders.
Solution: Implemented buffering strategy in useSensors:
const BUFFER_INTERVAL = 1000; // Update UI max once per second
// Buffer incoming data
bufferRef.current = newSensorData;
// Throttle UI updates
if (!timerRef.current) {
timerRef.current = setTimeout(() => {
setData(bufferRef.current); // Update UI with latest
timerRef.current = null;
}, BUFFER_INTERVAL);
}Benefits:
- Prevents render storms
- Always shows latest data
- Maintains 60fps UI
- Reduces CPU usage by 70%+
- β
Custom hooks for connection management (
useWebSocket,useSensors) - β Separation of concerns (transport vs. domain logic)
- β
useReffor persistent socket/buffer instances - β
useCallbackfor stable event handlers - β
useEffectfor lifecycle and cleanup - β Proper memory leak prevention
- β Runtime validation before state updates
- Node.js >= 18
- pnpm >= 8
# Clone the repository
git clone https://github.com/ChristySchott/iot-sensor-dashboard.git
cd iot-sensor-dashboard
# Install dependencies
pnpm install
# Start development servers (frontend + backend)
pnpm devThe app will be available at:
- Frontend: http://localhost:3000
- Backend: http://localhost:3001
- MQTT Broker: mqtt://broker.hivemq.com (public test broker)
The dashboard implements threshold-based alerting:
Temperature:
- π’ Normal: 18-27Β°C
- π‘ Warning: 15-18Β°C or 27-30Β°C
- π΄ Critical: <15Β°C or >30Β°C
Humidity:
- π’ Normal: 40-60%
- π‘ Warning: 30-40% or 60-70%
- π΄ Critical: <30% or >70%
Pressure:
- π’ Normal: 1000-1030 hPa
- π‘ Warning: 980-1000 hPa or 1030-1040 hPa
- π΄ Critical: <980 hPa or >1040 hPa
Light:
- π’ Normal: 100-800 lux
- π‘ Warning: <100 or >800 lux
Data Freshness:
- π’ Fresh: <5 seconds old
- π‘ Stale: 5-10 seconds old
- π΄ Very Stale: >10 seconds old
This project helped me understand:
- MQTT Protocol: Pub/Sub pattern, QoS levels, topic hierarchies
- IoT Architecture: Broker-mediated communication, device-to-cloud patterns
- Message Brokering: When to use MQTT vs HTTP for device communication
- Topic Design: Hierarchical topics (
sensors/+/temperature)
- Data Buffering: Handling high-frequency updates without UI degradation
- Throttling Strategies: When and how to throttle React state updates
- Memory Management: Preventing leaks in long-running connections
- Socket.IO: Event-based API, automatic reconnection, fallback mechanisms
- Connection Lifecycle: Managing connect/disconnect/reconnect states
- Type Safety: End-to-end TypeScript with shared event definitions
- Error Handling: Graceful degradation when connections fail
This is a learning/study project focused on MQTT and WebSocket fundamentals.
Not Included (intentionally):
- Authentication/Authorization
- Database persistence
- Unit/Integration tests
- Production deployment configuration
- Rate limiting
- User management
- Real IoT devices (uses simulator)
The focus is on understanding IoT communication patterns, MQTT protocol, and real-time data visualization with modern React applications.
Development Notes:
console.logstatements are intentionally left throughout the codebase for learning and debugging purposes. In production code, these would be replaced with a proper logging service (e.g., Winston, Pino) with appropriate log levels and structured logging.
Architecture:
Browser β WebSocket β Node.js β MQTT β Broker
Reasons:
- Security: Browsers can't connect to MQTT brokers directly (TCP limitation)
- Authentication: Future centralized auth layer in Node.js
- Data Transformation: Server can aggregate/filter before sending to clients
- Scalability: One MQTT connection serves many WebSocket clients
- Business Logic: Future data validation/processing before UI consumption
IoT sensors can send data at 1-10Hz. Without buffering:
- React re-renders 10 times per second per sensor
- 4 sensors = 40 re-renders/second
- UI becomes laggy, CPU usage spikes
With 1-second buffering:
- UI updates maximum once per second
- Always shows latest data
- Smooth 60fps experience
Runtime validation prevents:
- Invalid sensor data from breaking UI
- Type mismatches from external sources
- Malformed messages causing crashes
- Silent failures with wrong data types
Example:
const result = sensorDataSchema.safeParse(rawData);
if (!result.success) {
console.error("Invalid sensor data:", result.error);
return; // Don't update state with bad data
}
setData(result.data); // Type-safe, validated data