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);
  }
});