SDK API ReferenceReact Hooks

React Hooks API

Complete reference for all EarnLayer React hooks.

useEarnLayerClient

Main hook for accessing EarnLayer client functionality.

Usage

import { useEarnLayerClient } from '@earnlayer/sdk/react';
 
function ChatComponent() {
  const { 
    client,
    conversationId,
    isReady,
    initializeConversation
  } = useEarnLayerClient();
}

Returns

PropertyTypeDescription
clientEarnLayerClientEarnLayer client instance
conversationIdstring | nullCurrent conversation ID
isReadybooleanProvider initialized status
initializeConversation() => Promise<string>Create new conversation

Example

function ChatPage() {
  const { conversationId, initializeConversation } = useEarnLayerClient();
  
  useEffect(() => {
    // Initialize conversation on page load
    initializeConversation();
  }, []);
  
  const handleNewChat = async () => {
    // Create new conversation
    const newId = await initializeConversation();
    console.log('New conversation:', newId);
  };
  
  return (
    <div>
      <p>Conversation ID: {conversationId}</p>
      <button onClick={handleNewChat}>New Chat</button>
    </div>
  );
}

Example with Demo Mode

function ChatPage() {
  const { conversationId, initializeConversation } = useEarnLayerClient();
 
  useEffect(() => {
    // Initialize with demo mode for testing
    initializeConversation({
      demoMode: process.env.NODE_ENV === 'development'  // Auto-enable in dev
    });
  }, []);
 
  return (
    <div>
      <p>Conversation ID: {conversationId}</p>
      <p>Mode: {process.env.NODE_ENV === 'development' ? 'Demo' : 'Production'}</p>
    </div>
  );
}

useDisplayAd

Hook for fetching and managing display ads.

Usage

import { useDisplayAd } from '@earnlayer/sdk/react';
 
function DisplayAdComponent() {
  const { 
    ad,
    isLoading,
    error,
    refetch
  } = useDisplayAd({
    adType: 'banner'
  });
}

Parameters

ParameterTypeDefaultDescription
adType'hyperlink' | 'thinking' | 'banner''banner'Type of ad to fetch
autoFetchbooleantrueAutomatically fetch ads
thinkingAdTimeoutnumberundefinedThinking ads only: Seconds to wait for MCP to populate contextual ads. Recommended: 4. See Thinking Ads for details.
onAdFetched(ad: DisplayAd) => voidundefinedCallback when ad is fetched
onError(error: Error) => voidundefinedError callback

Returns

PropertyTypeDescription
adDisplayAd | nullCurrent ad object
isLoadingbooleanLoading state
errorError | nullError state
refetch() => Promise<void>Manually fetch new ad
refetchWithRefresh() => Promise<void>Fetch next ad from queue (excludes last served ad)

Example: Banner Ads

function DisplayAdComponent({ messageCount }: { messageCount: number }) {
  const { ad, isLoading, refetch } = useDisplayAd({
    adType: 'banner',
    onAdFetched: (ad) => {
      console.log('New ad loaded:', ad.title);
    }
  });
 
  // Refetch ad after each message
  useEffect(() => {
    if (messageCount > 0) {
      refetch();
    }
  }, [messageCount, refetch]);
 
  if (isLoading) return <div>Loading ad...</div>;
  if (!ad) return null;
 
  return (
    <a href={ad.url} target="_blank" rel="noopener noreferrer">
      <img src={ad.imageUrl} alt={ad.title} />
      <h3>{ad.title}</h3>
      <p>{ad.description}</p>
    </a>
  );
}

Example: Thinking Ads

function ThinkingAdComponent() {
  const { ad, isLoading } = useDisplayAd({
    adType: 'thinking',
    autoFetch: true,
    thinkingAdTimeout: 4,  // Wait up to 4 seconds for contextual ads
    onAdFetched: (ad) => {
      console.log('Thinking ad loaded:', {
        title: ad.title,
        source: ad.source  // 'queue' = contextual, 'fallback' = default
      });
    }
  });
 
  if (isLoading || !ad) return null;
 
  return (
    <a href={ad.url} target="_blank" rel="noopener noreferrer">
      <div className="flex items-center gap-2">
        <span className="animate-pulse">●</span>
        <span>{ad.title}</span>
      </div>
    </a>
  );
}
 
// Use in your chat during AI processing
{isThinking && <ThinkingAdComponent />}

Example: Auto-Refresh Banner Ads

Banner ads can automatically refresh to show different ads from the queue:

function AutoRefreshBannerAd() {
  const { ad, isLoading, refetchWithRefresh } = useDisplayAd({
    adType: 'banner',
    autoFetch: false
  });
 
  const [refreshCount, setRefreshCount] = useState(0);
  const MAX_REFRESHES = 3; // Show 4 total ads: initial + 3 refreshes
 
  // Auto-refresh every 5 seconds, stop after 3 refreshes
  useEffect(() => {
    if (!ad || isLoading || refreshCount >= MAX_REFRESHES) return;
 
    const timer = setTimeout(() => {
      refetchWithRefresh();
      setRefreshCount(prev => prev + 1);
    }, 5000); // 5 seconds
 
    return () => clearTimeout(timer);
  }, [ad?.id, isLoading, refreshCount, refetchWithRefresh]);
 
  // Reset count when manually fetching new ad
  const handleManualRefresh = () => {
    refetch();
    setRefreshCount(0);
  };
 
  if (isLoading || !ad) return null;
 
  return (
    <a href={ad.url} target="_blank" rel="noopener noreferrer">
      <img src={ad.imageUrl} alt={ad.title} />
      <h3>{ad.title}</h3>
      <p>{ad.description}</p>
    </a>
  );
}

Key Points:

  • refetchWithRefresh() excludes the last served ad from results
  • Use refreshCount to limit number of refreshes
  • Timer resets when ad changes (new ad loaded)
  • Recommended: 3-5 refreshes max to avoid overwhelming users

useEarnLayer

Legacy hook for backward compatibility. Use useEarnLayerClient instead.

Usage

import { useEarnLayer } from '@earnlayer/sdk/react';
 
function ChatComponent() {
  const { 
    conversationId,
    refreshAds
  } = useEarnLayer();
}

Returns

PropertyTypeDescription
conversationIdstring | nullCurrent conversation ID
refreshAds() => voidRefresh all ads

Example

function ChatWithRefresh() {
  const { conversationId, refreshAds } = useEarnLayer();
  
  const sendMessage = async (message: string) => {
    // Send message logic
    await fetch('/api/chat', {
      method: 'POST',
      body: JSON.stringify({ message, conversationId })
    });
    
    // Refresh ads after message
    refreshAds();
  };
  
  return (
    <div>
      <button onClick={() => sendMessage('Hello')}>
        Send Message
      </button>
    </div>
  );
}

DisplayAd Object

Structure of ad objects returned by useDisplayAd.

Properties

PropertyTypeDescription
idstringUnique ad identifier
impressionIdstringImpression tracking ID
titlestringAd title
descriptionstring | undefinedAd description
urlstringClick URL (includes tracking)
imageUrlstring | undefinedAd image URL
adType'hyperlink' | 'thinking' | 'banner'Ad type
source'queue' | 'fallback'Ad source

Example

interface DisplayAd {
  id: 'ad_123';
  impressionId: 'imp_456';
  title: 'Asana - Project Management';
  description: 'Organize your team\'s work';
  url: 'https://asana.com?utm_source=earnlayer&impression_id=imp_456';
  imageUrl: 'https://example.com/asana.jpg';
  adType: 'banner';
  source: 'queue';
}

Error Handling

Common Error Types

interface EarnLayerError {
  message: string;
  code: string;
  details?: any;
}
 
// Handle errors in hooks
const { ad, error } = useDisplayAd({
  onError: (error: EarnLayerError) => {
    console.error('Ad fetch failed:', error.message);
    
    switch (error.code) {
      case 'RATE_LIMITED':
        // Handle rate limiting
        break;
      case 'INVALID_API_KEY':
        // Handle invalid API key
        break;
      case 'QUOTA_EXCEEDED':
        // Handle quota exceeded
        break;
    }
  }
});

Error Recovery

function ResilientDisplayAd() {
  const [retryCount, setRetryCount] = useState(0);
  const maxRetries = 3;
  
  const { ad, error, refetch } = useDisplayAd({
    onError: (error) => {
      if (retryCount < maxRetries) {
        setTimeout(() => {
          setRetryCount(prev => prev + 1);
          refetch();
        }, 1000 * retryCount);
      }
    }
  });
  
  if (error && retryCount >= maxRetries) {
    return <div>Ad temporarily unavailable</div>;
  }
  
  return ad ? <DisplayAdComponent ad={ad} /> : <div>Loading...</div>;
}

Performance Optimization

Memoization

import { memo, useMemo } from 'react';
 
const DisplayAdComponent = memo(() => {
  const { ad } = useDisplayAd({});
  
  const memoizedAd = useMemo(() => {
    if (!ad) return null;
    
    return (
      <a href={ad.url} target="_blank">
        <img src={ad.imageUrl} alt={ad.title} />
        <h3>{ad.title}</h3>
      </a>
    );
  }, [ad]);
  
  return memoizedAd;
});

Conditional Rendering

function ConditionalAd({ userType }: { userType: string }) {
  // Only show ads to free users
  if (userType === 'premium') {
    return null;
  }
  
  return <DisplayAd />;
}

Lazy Loading

import { lazy, Suspense } from 'react';
 
const DisplayAd = lazy(() => 
  import('@earnlayer/sdk/react').then(m => ({ default: m.DisplayAd }))
);
 
function ChatPage() {
  return (
    <Suspense fallback={<div>Loading ad...</div>}>
      <DisplayAd />
    </Suspense>
  );
}

Best Practices

1. Always Handle Loading States

function DisplayAdWithLoading() {
  const { ad, isLoading, error } = useDisplayAd({});
  
  if (isLoading) return <div>Loading ad...</div>;
  if (error) return <div>Ad unavailable</div>;
  if (!ad) return null;
  
  return <DisplayAdComponent ad={ad} />;
}

2. Use Conversation Context

function ContextualAd() {
  const { conversationId } = useEarnLayerClient();
  
  // Only show ads when conversation is active
  if (!conversationId) return null;
  
  return <DisplayAd />;
}

3. Implement Proper Cleanup

function AdWithCleanup() {
  const { ad, refetch } = useDisplayAd({});
  
  useEffect(() => {
    const interval = setInterval(refetch, 30000);
    return () => clearInterval(interval);
  }, [refetch]);
  
  return <DisplayAdComponent ad={ad} />;
}

4. Track Performance

function TrackedDisplayAd() {
  const { ad, isLoading } = useDisplayAd({
    onAdFetched: (ad) => {
      // Track ad performance
      analytics.track('ad_impression', {
        ad_id: ad.id,
        source: ad.source
      });
    }
  });
  
  return <DisplayAdComponent ad={ad} />;
}

Next Steps