Skip to main content

Integration overview

Learn how to integrate DexPaprika’s streaming API into React and Next.js applications with:
  • Custom React hooks for streaming
  • TypeScript support
  • Server-side rendering considerations
  • Production-ready error handling

Finding Token Addresses

Before streaming, you need token addresses. Use the REST API:

Search Tokens

Use the Search API to find tokens by name or symbol

Get Networks

Use the Networks API to get supported chains
# Search for USDC tokens
curl "https://api.dexpaprika.com/search?query=USDC"

# Get all supported networks
curl "https://api.dexpaprika.com/networks"

Quick Example

import { useEffect, useState } from 'react';

function useCryptoPrice(chain: string, address: string) {
  const [price, setPrice] = useState<number | null>(null);
  const [status, setStatus] = useState<'connecting' | 'connected' | 'error'>('connecting');

  useEffect(() => {
    const url = new URL('https://streaming.dexpaprika.com/stream');
    url.searchParams.set('method', 't_p');
    url.searchParams.set('chain', chain);
    url.searchParams.set('address', address);

    const eventSource = new EventSource(url.toString());

    eventSource.onopen = () => setStatus('connected');

    eventSource.addEventListener('t_p', (event) => {
      const data = JSON.parse(event.data);
      setPrice(parseFloat(data.p));
    });

    eventSource.onerror = () => setStatus('error');

    return () => eventSource.close();
  }, [chain, address]);

  return { price, status };
}

// Usage
function PriceDisplay() {
  const { price, status } = useCryptoPrice('ethereum', '0xc02aa...');

  return (
    <div>
      {status === 'connected' && price && (
        <span>${price.toFixed(2)}</span>
      )}
    </div>
  );
}

Complete Implementation

1. Install Dependencies

npm install --save-dev @types/react

2. Create the Streaming Hook

Create hooks/useCryptoStream.ts:

3. Create Price Display Component

Create components/CryptoPriceDisplay.tsx:

4. Multi-Asset Streaming

For streaming multiple assets, create hooks/useMultiCryptoStream.ts:

Next.js Specific Considerations

App Router (Next.js 13+)

For Next.js App Router, ensure streaming components are client-side:
// app/components/LivePrices.tsx
'use client';

import { CryptoPriceDisplay } from './CryptoPriceDisplay';

export default function LivePrices() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
      <CryptoPriceDisplay
        chain="ethereum"
        address="0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
        symbol="WETH"
      />
      <CryptoPriceDisplay
        chain="solana"
        address="So11111111111111111111111111111111111111112"
        symbol="SOL"
      />
      <CryptoPriceDisplay
        chain="bsc"
        address="0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c"
        symbol="BNB"
      />
    </div>
  );
}

Pages Router

For traditional Pages Router, handle SSR properly:
// pages/prices.tsx
import dynamic from 'next/dynamic';

const LivePrices = dynamic(() => import('../components/LivePrices'), {
  ssr: false, // Disable SSR for streaming components
  loading: () => <p>Loading prices...</p>,
});

export default function PricesPage() {
  return (
    <div>
      <h1>Live Crypto Prices</h1>
      <LivePrices />
    </div>
  );
}

Advanced Patterns

1. Context Provider for Global Streaming

// contexts/CryptoStreamContext.tsx
import React, { createContext, useContext } from 'react';
import { useMultiCryptoStream } from '../hooks/useMultiCryptoStream';

const CryptoStreamContext = createContext<ReturnType<typeof useMultiCryptoStream> | null>(null);

export function CryptoStreamProvider({ children }: { children: React.ReactNode }) {
  const streamData = useMultiCryptoStream({
    assets: [
      { chain: 'ethereum', address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', method: 't_p' },
      { chain: 'solana', address: 'So11111111111111111111111111111111111111112', method: 't_p' },
      // Add more assets as needed
    ],
  });

  return (
    <CryptoStreamContext.Provider value={streamData}>
      {children}
    </CryptoStreamContext.Provider>
  );
}

export function useCryptoStreamContext() {
  const context = useContext(CryptoStreamContext);
  if (!context) {
    throw new Error('useCryptoStreamContext must be used within CryptoStreamProvider');
  }
  return context;
}

2. Optimistic UI Updates

function OptimisticPriceDisplay({ chain, address }: Props) {
  const [displayPrice, setDisplayPrice] = useState<number | null>(null);
  const [isStale, setIsStale] = useState(false);

  const { price, lastUpdate } = useCryptoStream({
    chain,
    address,
    onPrice: (newPrice) => {
      setDisplayPrice(newPrice);
      setIsStale(false);
    },
  });

  useEffect(() => {
    // Mark as stale if no update for 5 seconds
    const timer = setTimeout(() => setIsStale(true), 5000);
    return () => clearTimeout(timer);
  }, [lastUpdate]);

  return (
    <div className={isStale ? 'opacity-50' : ''}>
      ${displayPrice?.toFixed(2) ?? '---'}
      {isStale && <span className="text-yellow-500 text-xs">⚠️ Stale</span>}
    </div>
  );
}

3. Performance Optimization with React.memo

const PriceCell = React.memo(({ price, symbol }: { price: number; symbol: string }) => {
  return (
    <div className="price-cell">
      <span className="symbol">{symbol}</span>
      <span className="price">${price.toFixed(2)}</span>
    </div>
  );
}, (prevProps, nextProps) => {
  // Only re-render if price changes by more than 0.01%
  return Math.abs(prevProps.price - nextProps.price) / prevProps.price < 0.0001;
});

Testing

Unit Testing with Jest


Production Checklist

Wrap streaming components in error boundaries to handle failures gracefully:
import { ErrorBoundary } from 'react-error-boundary';

<ErrorBoundary fallback={<div>Price unavailable</div>}>
  <CryptoPriceDisplay {...props} />
</ErrorBoundary>
Always clean up EventSource connections and timeouts:
useEffect(() => {
  const eventSource = new EventSource(url);

  // Cleanup function
  return () => {
    eventSource.close();
    clearTimeout(reconnectTimeout);
  };
}, []);
Monitor connection status and alert on failures:
useEffect(() => {
  if (status === 'error') {
    // Send to error tracking service
    Sentry.captureException(new Error('Stream connection failed'));
  }
}, [status]);
Consider lazy loading streaming components:
const LivePrices = lazy(() => import('./LivePrices'));

Get Support