Reactive Components
Real-world examples of real-time reactive components using DBindly v2.4.0.
These examples demonstrate how to build real-time reactive components that automatically update when data changes on the server.
Setup
First, enable reactivity in your initialization:
DBindly.init({
apiUrl: 'https://your-api.com/api',
apiKey: 'pk_live_xxx',
reactive: true,
reactiveMode: 'auto', // Uses WebSocket → SSE → Polling fallback
});1. Live Stock Price Ticker
A stock ticker that updates in real-time when prices change.
<div class="stock-ticker">
<!-- Each stock auto-subscribes to its data ID -->
<div dbind_reactive="stock-AAPL" class="stock-card">
<div class="stock-symbol" dbind_data="symbol"></div>
<div class="stock-price" dbind_data="price" dbind_format="currency:USD"></div>
<div class="stock-change">
<span dbind_data="changePercent" dbind_format="percent:2"></span>
<span dbind_show="isUp" class="up">📈</span>
<span dbind_show="isDown" class="down">📉</span>
</div>
</div>
<div dbind_reactive="stock-GOOGL" class="stock-card">
<div class="stock-symbol" dbind_data="symbol"></div>
<div class="stock-price" dbind_data="price" dbind_format="currency:USD"></div>
<div class="stock-change">
<span dbind_data="changePercent" dbind_format="percent:2"></span>
<span dbind_show="isUp" class="up">📈</span>
<span dbind_show="isDown" class="down">📉</span>
</div>
</div>
</div>
<style>
.stock-card { padding: 1rem; border: 1px solid #e5e7eb; border-radius: 8px; }
.stock-symbol { font-weight: bold; font-size: 1.25rem; }
.stock-price { font-size: 1.5rem; }
.up { color: #22c55e; }
.down { color: #ef4444; }
</style>2. Real-Time Notification Counter
A notification badge that updates instantly when new notifications arrive.
<nav class="navbar">
<div dbind_reactive="user-notifications" class="notifications">
<button class="notification-btn">
<svg class="bell-icon" viewBox="0 0 24 24" width="24" height="24">
<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/>
</svg>
<!-- Badge shows count, hidden when 0 -->
<span
dbind_data="unreadCount"
dbind_show="unreadCount"
class="badge"
></span>
</button>
<!-- Dropdown with notification list -->
<div class="dropdown" dbind_show="hasNotifications">
<div dbind_repeat="notifications" class="notification-item">
<p dbind_data="message"></p>
<time dbind_data="createdAt" dbind_format="date:relative"></time>
</div>
<div dbind_hide="hasNotifications" class="empty">
No new notifications
</div>
</div>
</div>
</nav>
<style>
.notification-btn { position: relative; }
.badge {
position: absolute;
top: -5px;
right: -5px;
background: #ef4444;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
</style>3. Live Chat Messages
A chat interface where messages appear instantly for all connected users.
<div dbind_reactive="chat-room-123" class="chat-container">
<!-- Chat header with live user count -->
<div class="chat-header">
<h3 dbind_data="roomName"></h3>
<span class="online-count">
<span dbind_data="onlineCount"></span> online
</span>
</div>
<!-- Messages list - updates in real-time -->
<div class="messages-container">
<div dbind_repeat="messages" class="message">
<img dbind_src="sender.avatar" dbind_alt="sender.name" class="avatar">
<div class="message-content">
<div class="message-header">
<strong dbind_data="sender.name"></strong>
<time dbind_data="timestamp" dbind_format="date:relative"></time>
</div>
<p dbind_data="text"></p>
</div>
</div>
</div>
<!-- Typing indicator -->
<div dbind_show="isTyping" class="typing-indicator">
<span dbind_data="typingUser"></span> is typing...
</div>
</div>
<style>
.chat-container { display: flex; flex-direction: column; height: 500px; }
.messages-container { flex: 1; overflow-y: auto; padding: 1rem; }
.message { display: flex; gap: 0.75rem; margin-bottom: 1rem; }
.avatar { width: 40px; height: 40px; border-radius: 50%; }
.typing-indicator { padding: 0.5rem; color: #6b7280; font-style: italic; }
</style>4. Live Dashboard Stats
A dashboard with multiple independently updating stat cards.
<div class="dashboard-grid">
<!-- Revenue Card -->
<div dbind_reactive="stats-revenue" class="stat-card">
<div class="stat-icon">💰</div>
<div class="stat-content">
<h4>Total Revenue</h4>
<div class="stat-value" dbind_data="total" dbind_format="currency:USD"></div>
<div class="stat-change">
<span dbind_data="changePercent" dbind_format="percent:1"></span>
<span dbind_show="isPositive" class="positive">↑</span>
<span dbind_hide="isPositive" class="negative">↓</span>
vs last month
</div>
</div>
</div>
<!-- Active Users Card -->
<div dbind_reactive="stats-users" class="stat-card">
<div class="stat-icon">👥</div>
<div class="stat-content">
<h4>Active Users</h4>
<div class="stat-value" dbind_data="count" dbind_format="number"></div>
<div class="stat-change">
<span dbind_data="newToday"></span> new today
</div>
</div>
</div>
<!-- Orders Card -->
<div dbind_reactive="stats-orders" class="stat-card">
<div class="stat-icon">📦</div>
<div class="stat-content">
<h4>Orders Today</h4>
<div class="stat-value" dbind_data="count"></div>
<div class="stat-change">
<span dbind_data="pendingCount"></span> pending
</div>
</div>
</div>
<!-- Server Status Card -->
<div dbind_reactive="stats-server" class="stat-card">
<div class="stat-icon">🖥️</div>
<div class="stat-content">
<h4>Server Status</h4>
<div class="stat-value">
<span dbind_show="isHealthy" class="status-healthy">● Healthy</span>
<span dbind_hide="isHealthy" class="status-unhealthy">● Issues</span>
</div>
<div class="stat-change">
Uptime: <span dbind_data="uptime" dbind_format="percent:2"></span>
</div>
</div>
</div>
</div>
<style>
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
.stat-card {
display: flex;
gap: 1rem;
padding: 1.5rem;
background: white;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stat-icon { font-size: 2rem; }
.stat-value { font-size: 1.75rem; font-weight: bold; }
.stat-change { color: #6b7280; font-size: 0.875rem; }
.positive { color: #22c55e; }
.negative { color: #ef4444; }
.status-healthy { color: #22c55e; }
.status-unhealthy { color: #ef4444; }
</style>5. Live Product Inventory
An e-commerce product card with real-time stock updates.
<div dbind_reactive="product-123" class="product-card">
<img dbind_src="image" dbind_alt="name" class="product-image">
<div class="product-info">
<h2 dbind_data="name"></h2>
<p dbind_data="description" dbind_format="truncate:100"></p>
<div class="product-price">
<span class="current-price" dbind_data="price" dbind_format="currency:USD"></span>
<span dbind_show="onSale" class="original-price" dbind_data="originalPrice" dbind_format="currency:USD"></span>
<span dbind_show="onSale" class="discount-badge">
<span dbind_data="discountPercent"></span>% OFF
</span>
</div>
<!-- Stock status - updates in real-time -->
<div class="stock-status">
<span dbind_show="inStock" class="in-stock">
✓ In Stock (<span dbind_data="stock"></span> available)
</span>
<span dbind_hide="inStock" class="out-of-stock">
✗ Out of Stock
</span>
<!-- Low stock warning -->
<span dbind_if="stock < 5 && stock > 0" class="low-stock-warning">
⚠️ Only <span dbind_data="stock"></span> left - order soon!
</span>
</div>
<!-- Add to cart button - disabled when out of stock -->
<button
class="add-to-cart"
dbind_class="inStock ? '' : 'disabled'"
>
<span dbind_show="inStock">Add to Cart</span>
<span dbind_hide="inStock">Notify When Available</span>
</button>
</div>
</div>
<style>
.product-card { max-width: 400px; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
.product-image { width: 100%; height: 300px; object-fit: cover; }
.product-info { padding: 1.5rem; }
.current-price { font-size: 1.5rem; font-weight: bold; }
.original-price { text-decoration: line-through; color: #9ca3af; }
.discount-badge { background: #ef4444; color: white; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; }
.in-stock { color: #22c55e; }
.out-of-stock { color: #ef4444; }
.low-stock-warning { display: block; color: #f59e0b; margin-top: 0.5rem; }
.add-to-cart { width: 100%; padding: 1rem; background: #3b82f6; color: white; border: none; border-radius: 8px; font-size: 1rem; cursor: pointer; }
.add-to-cart.disabled { background: #9ca3af; cursor: not-allowed; }
</style>6. JavaScript Subscription Examples
Basic Subscription
// Initialize with reactivity enabled
DBindly.init({
apiUrl: 'https://api.example.com',
apiKey: 'pk_live_xxx',
reactive: true,
});
// Subscribe to a data ID with callback
const unsubscribe = DBindly.subscribe('order-status', (data, action) => {
if (action === 'update') {
console.log('Order status updated:', data.status);
showToast(`Order is now: ${data.status}`);
} else if (action === 'delete') {
console.log('Order was cancelled');
}
});
// Later: unsubscribe when no longer needed
unsubscribe();Collection Subscription
// Subscribe to a live leaderboard
const unsubscribe = DBindly.subscribeCollection(
'leaderboard',
{
limit: 10,
sort: { field: 'score', direction: 'desc' }
},
(result, action) => {
console.log('Leaderboard updated!');
console.log('Top player:', result.data[0]?.name);
}
);Event Listeners
// Listen for connection events
document.addEventListener('dbindly:reactive-connect', (e) => {
console.log('Connected via:', e.detail.mode); // 'websocket', 'sse', or 'polling'
showConnectionStatus('connected');
});
document.addEventListener('dbindly:reactive-disconnect', (e) => {
console.log('Disconnected');
showConnectionStatus('reconnecting...');
});
// Listen for data updates
document.addEventListener('dbindly:data-updated', (e) => {
const { dataId, data, action } = e.detail;
// Flash the updated element
const element = document.querySelector(`[dbind_reactive="${dataId}"]`);
if (element) {
element.classList.add('dbindly-updated');
setTimeout(() => element.classList.remove('dbindly-updated'), 500);
}
});React Integration
import { useEffect, useState } from 'react';
function LivePrice({ productId }) {
const [price, setPrice] = useState(null);
useEffect(() => {
// Subscribe when component mounts
const unsubscribe = DBindly.subscribe(`product-${productId}`, (data) => {
setPrice(data.price);
});
// Unsubscribe when component unmounts
return () => unsubscribe();
}, [productId]);
return (
<div className="live-price">
{price ? `$${price.toFixed(2)}` : 'Loading...'}
</div>
);
}Check Connection Status
// Get current reactive status
const status = DBindly.getReactiveStatus();
console.log(status);
// {
// connected: true,
// mode: 'websocket',
// subscriptions: ['product-123', 'order-456'],
// collectionSubscriptions: ['leaderboard']
// }
// Display in UI
if (status.connected) {
showStatus(`Connected via ${status.mode}`);
} else {
showStatus('Disconnected');
}Custom Update Animations
Add visual feedback when data updates:
/* Flash effect on update */
.dbindly-updated {
animation: highlight-update 0.5s ease;
}
@keyframes highlight-update {
0% {
background-color: rgba(59, 130, 246, 0.3);
transform: scale(1.02);
}
100% {
background-color: transparent;
transform: scale(1);
}
}
/* Pulse effect for important updates */
[dbind_reactive].important-update {
animation: pulse 0.3s ease 2;
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
50% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); }
}// Add custom class on update
document.addEventListener('dbindly:data-updated', (e) => {
const el = document.querySelector(`[dbind_reactive="${e.detail.dataId}"]`);
if (el && e.detail.data.priority === 'high') {
el.classList.add('important-update');
setTimeout(() => el.classList.remove('important-update'), 600);
}
});