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
| Property | Type | Description |
|---|---|---|
client | EarnLayerClient | EarnLayer client instance |
conversationId | string | null | Current conversation ID |
isReady | boolean | Provider 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
| Parameter | Type | Default | Description |
|---|---|---|---|
adType | 'hyperlink' | 'thinking' | 'banner' | 'banner' | Type of ad to fetch |
autoFetch | boolean | true | Automatically fetch ads |
thinkingAdTimeout | number | undefined | Thinking ads only: Seconds to wait for MCP to populate contextual ads. Recommended: 4. See Thinking Ads for details. |
onAdFetched | (ad: DisplayAd) => void | undefined | Callback when ad is fetched |
onError | (error: Error) => void | undefined | Error callback |
Returns
| Property | Type | Description |
|---|---|---|
ad | DisplayAd | null | Current ad object |
isLoading | boolean | Loading state |
error | Error | null | Error 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
refreshCountto 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
| Property | Type | Description |
|---|---|---|
conversationId | string | null | Current conversation ID |
refreshAds | () => void | Refresh 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
| Property | Type | Description |
|---|---|---|
id | string | Unique ad identifier |
impressionId | string | Impression tracking ID |
title | string | Ad title |
description | string | undefined | Ad description |
url | string | Click URL (includes tracking) |
imageUrl | string | undefined | Ad 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
- Display Ads Component - Component documentation
- Thinking Ads Component - Loading state ads
- Troubleshooting - Common issues and solutions
- Quick Start Guide - Complete setup guide