Build real-time financial applications in React using WebSocket connections. Perfect for trading dashboards, portfolio trackers, and real-time news feeds that require instant market updates.
WebSocket Requirement: WebSocket streaming requires Pro Plan ($99/month) or Enterprise Plan. Upgrade here.

Basic Connection

import { useState, useEffect } from 'react';
import { io } from 'socket.io-client';

function useByulNews() {
  const [news, setNews] = useState([]);
  const [connected, setConnected] = useState(false);

  useEffect(() => {
    const socket = io('wss://api.byul.ai/news-v2', {
      auth: { apiKey: process.env.REACT_APP_BYUL_API_KEY }
    });

    socket.on('connect', () => {
      setConnected(true);
      socket.emit('news:subscribe', { minImportance: 7, startDate: '2024-01-01T00:00:00.000Z' });
    });

    socket.on('disconnect', () => {
      setConnected(false);
    });

    socket.on('news:data', (response) => {
      const news = response.data.news;
      setNews(prev => [...news, ...prev].slice(0, 50));
    });

    return () => socket.close();
  }, []);

  return { news, connected };
}

// Usage
function NewsApp() {
  const { news, connected } = useByulNews();

  return (
    <div>
      <h1>News Feed {connected ? 'Connected' : 'Disconnected'}</h1>
      {news.map(article => (
        <div key={article._id} style={{ border: '1px solid #ddd', padding: 10, margin: 10 }}>
          <h3>{article.title}</h3>
          <p>Importance: {article.importanceScore}/10</p>
          <p>Symbols: {article.symbols?.join(', ') || 'General'}</p>
        </div>
      ))}
    </div>
  );
}

React Integration

Context Provider for Global State

import React, { createContext, useContext, useState, useEffect } from 'react';
import { io } from 'socket.io-client';

const NewsContext = createContext();

export function NewsProvider({ children }) {
  const [news, setNews] = useState([]);
  const [connected, setConnected] = useState(false);
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const newSocket = io('wss://api.byul.ai/news-v2');

    newSocket.on('connect', () => {
      setConnected(true);
      newSocket.emit('news:subscribe', { minImportance: 6, startDate: '2024-01-01T00:00:00.000Z' });
    });

    newSocket.on('disconnect', () => {
      setConnected(false);
    });

    newSocket.on('news:data', (response) => {
      const newsItems = response.data.news;
      setNews(prev => [...newsItems, ...prev].slice(0, 100));
    });

    setSocket(newSocket);
    return () => newSocket.close();
  }, []);

  const subscribeToSymbol = (symbol) => {
    if (socket && connected) {
      socket.emit('news:subscribe', { symbol, minImportance: 6, startDate: '2024-01-01T00:00:00.000Z' });
    }
  };

  const unsubscribeFromSymbol = (symbol) => {
    if (socket && connected) {
      socket.emit('news:unsubscribe', { symbol });
    }
  };

  return (
    <NewsContext.Provider value={{
      news,
      connected,
      subscribeToSymbol,
      unsubscribeFromSymbol
    }}>
      {children}
    </NewsContext.Provider>
  );
}

export const useNews = () => useContext(NewsContext);

Advanced Examples

Real-time News Dashboard

import React, { useState, useEffect } from 'react';
import { io } from 'socket.io-client';

function NewsDashboard() {
  const [articles, setArticles] = useState([]);
  const [status, setStatus] = useState('disconnected');

  useEffect(() => {
    const socket = io('wss://api.byul.ai/news-v2', {
      auth: { apiKey: process.env.REACT_APP_BYUL_API_KEY }
    });

    socket.on('connect', () => {
      setStatus('connected');
      socket.emit('news:subscribe', { minImportance: 6, startDate: '2024-01-01T00:00:00.000Z' });
    });

    socket.on('disconnect', () => {
      setStatus('disconnected');
    });

    socket.on('news:data', (response) => {
      const news = response.data.news;
      setArticles(prev => [...news, ...prev].slice(0, 100));
    });

    return () => socket.close();
  }, []);

  return (
    <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
      <div style={{ 
        display: 'flex', 
        justifyContent: 'space-between', 
        alignItems: 'center',
        marginBottom: '20px'
      }}>
        <h1>Byul AI News</h1>
        <div style={{ 
          padding: '5px 10px', 
          borderRadius: '5px',
          backgroundColor: status === 'connected' ? '#d4edda' : '#f8d7da',
          color: status === 'connected' ? '#155724' : '#721c24'
        }}>
          {status === 'connected' ? 'Connected' : 'Disconnected'}
        </div>
      </div>

      {articles.map(article => (
        <div key={article._id} style={{
          border: '1px solid #e0e0e0',
          borderRadius: '8px',
          padding: '15px',
          marginBottom: '15px',
          backgroundColor: 'white'
        }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'start' }}>
            <h3 style={{ margin: '0 0 10px 0', flex: 1 }}>{article.title}</h3>
            <div style={{ display: 'flex', gap: '10px', alignItems: 'center' }}>
              {article.symbols && article.symbols.length > 0 && (
                <span style={{
                  backgroundColor: '#007bff',
                  color: 'white',
                  padding: '2px 8px',
                  borderRadius: '3px',
                  fontSize: '0.8em'
                }}>
                  {article.symbols.join(', ')}
                </span>
              )}
              <span style={{
                backgroundColor: '#f8f9fa',
                padding: '2px 8px',
                borderRadius: '3px',
                fontSize: '0.8em',
                fontWeight: 'bold'
              }}>
                {article.importanceScore}/10
              </span>
            </div>
          </div>
          
          <p style={{ color: '#666', fontSize: '0.9em', margin: '10px 0' }}>
            {new Date(article.date).toLocaleString()}
          </p>
          
          <a 
            href={article.url} 
            target="_blank" 
            rel="noopener noreferrer"
            style={{ color: '#007bff', textDecoration: 'none' }}
          >
            Read more →
          </a>
        </div>
      ))}

      {articles.length === 0 && (
        <div style={{ textAlign: 'center', color: '#666', padding: '50px' }}>
          Waiting for news updates...
        </div>
      )}
    </div>
  );
}

export default NewsDashboard;

Portfolio Tracker

import React, { useState, useEffect } from 'react';
import { io } from 'socket.io-client';

function PortfolioTracker() {
  const [portfolio, setPortfolio] = useState(['AAPL', 'GOOGL', 'MSFT']);
  const [news, setNews] = useState([]);
  const [newSymbol, setNewSymbol] = useState('');
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    const newSocket = io('wss://api.byul.ai/news-v2');

    newSocket.on('connect', () => {
      // Subscribe to portfolio news
      portfolio.forEach(symbol => {
        newSocket.emit('news:subscribe', { 
          symbol, 
          minImportance: 6,
          startDate: '2024-01-01T00:00:00.000Z',
          endDate: '2024-01-31T23:59:59.999Z'
        });
      });
    });

    newSocket.on('news:data', (response) => {
      const news = response.data.news;
      const portfolioNews = news.filter(article => 
        article.symbols && article.symbols.some(symbol => portfolio.includes(symbol))
      );
      setNews(prev => [...portfolioNews, ...prev].slice(0, 50));
    });

    setSocket(newSocket);
    return () => newSocket.close();
  }, [portfolio]);

  const addSymbol = () => {
    if (newSymbol && !portfolio.includes(newSymbol.toUpperCase())) {
      const symbol = newSymbol.toUpperCase();
      setPortfolio(prev => [...prev, symbol]);
      
      if (socket) {
        socket.emit('news:subscribe', { symbol, minImportance: 6, startDate: '2024-01-01T00:00:00.000Z', endDate: '2024-01-31T23:59:59.999Z' });
      }
      
      setNewSymbol('');
    }
  };

  const removeSymbol = (symbol) => {
    setPortfolio(prev => prev.filter(s => s !== symbol));
    if (socket) {
      socket.emit('news:unsubscribe', { symbol });
    }
  };

  return (
    <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
      <h1>Portfolio News Tracker</h1>

      <div style={{ marginBottom: '20px' }}>
        <h3>Portfolio ({portfolio.length} symbols)</h3>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', marginBottom: '10px' }}>
          {portfolio.map(symbol => (
            <div key={symbol} style={{
              display: 'flex',
              alignItems: 'center',
              backgroundColor: '#f8f9fa',
              padding: '5px 10px',
              borderRadius: '5px',
              border: '1px solid #dee2e6'
            }}>
              {symbol}
              <button 
                onClick={() => removeSymbol(symbol)}
                style={{
                  marginLeft: '5px',
                  background: 'none',
                  border: 'none',
                  cursor: 'pointer',
                  color: '#dc3545'
                }}
              >
                ×
              </button>
            </div>
          ))}
        </div>
        
        <div style={{ display: 'flex', gap: '10px' }}>
          <input
            type="text"
            placeholder="Add symbol (e.g., TSLA)"
            value={newSymbol}
            onChange={(e) => setNewSymbol(e.target.value)}
            style={{ padding: '5px', borderRadius: '3px', border: '1px solid #ddd' }}
          />
          <button onClick={addSymbol} style={{
            padding: '5px 15px',
            backgroundColor: '#007bff',
            color: 'white',
            border: 'none',
            borderRadius: '3px',
            cursor: 'pointer'
          }}>
            Add
          </button>
        </div>
      </div>

      <h3>Recent News ({news.length})</h3>
      {news.map(article => (
        <div key={article._id} style={{
          border: '1px solid #e0e0e0',
          borderLeft: '4px solid #007bff',
          padding: '15px',
          marginBottom: '10px',
          backgroundColor: 'white'
        }}>
          <div style={{ display: 'flex', justifyContent: 'space-between' }}>
            <h4 style={{ margin: 0 }}>{article.symbols?.join(', ') || 'General'}</h4>
            <span style={{ fontSize: '0.8em', color: '#666' }}>
              {article.importanceScore}/10
            </span>
          </div>
          <h5 style={{ margin: '5px 0' }}>{article.title}</h5>
          <p style={{ fontSize: '0.8em', color: '#666' }}>
            {new Date(article.date).toLocaleString()}
          </p>
        </div>
      ))}
    </div>
  );
}

export default PortfolioTracker;

Error Handling

Error Handling Hook

import { useState, useEffect } from 'react';
import { io } from 'socket.io-client';

function useByulNewsWithErrors() {
  const [news, setNews] = useState([]);
  const [status, setStatus] = useState('disconnected');
  const [error, setError] = useState(null);

  useEffect(() => {
    const socket = io('wss://api.byul.ai/news-v2', {
      auth: { apiKey: process.env.REACT_APP_BYUL_API_KEY }
    });

    socket.on('connect', () => {
      setStatus('connected');
      setError(null);
      socket.emit('news:subscribe', { minImportance: 7, startDate: '2024-01-01T00:00:00.000Z' });
    });

    socket.on('disconnect', () => {
      setStatus('disconnected');
    });

    socket.on('connect_error', (err) => {
      setError(`Connection failed: ${err.message}`);
      setStatus('error');
    });

    socket.on('news:data', (response) => {
      const news = response.data.news;
      setNews(prev => [...news, ...prev].slice(0, 50));
    });

    return () => socket.close();
  }, []);

  return { news, status, error };
}

Connection Status Component

function ConnectionStatus({ status, error }) {
  const getStatusColor = () => {
    switch (status) {
      case 'connected': return '#4caf50';
      case 'error': return '#f44336';
      default: return '#ff9800';
    }
  };

  return (
    <div style={{
      padding: '8px 16px',
      borderRadius: '4px',
      backgroundColor: getStatusColor(),
      color: 'white',
      fontSize: '14px',
      fontWeight: 'bold'
    }}>
      {status === 'connected' && 'Connected'}
      {status === 'disconnected' && 'Disconnected'}
      {status === 'error' && `Error: ${error}`}
    </div>
  );
}

Environment Setup

Installation

npm install socket.io-client

Environment Variables

Create .env file:
REACT_APP_BYUL_API_KEY=byul_api_key

Production Configuration

// config/websocket.js
export const websocketConfig = {
  url: process.env.REACT_APP_WEBSOCKET_URL || 'wss://api.byul.ai/news-v2',
  apiKey: process.env.REACT_APP_BYUL_API_KEY,
  reconnectionAttempts: 5,
  reconnectionDelay: 1000,
  timeout: 20000
};

Next Steps