{ad.brand_name}
{ad.product_name}
{ad.description}
{ad.price_range && {ad.price_range}}
React SDK for AdsOverAI - Native advertising for AI chat interfaces
npm install @adsoverai/react



Quick Start โข Documentation โข Examples โข API Reference โข Support
---
- Overview
- Features
- Installation
- npm/pnpm/yarn
- CDN Installation
- Quick Start
- Basic Integration
- With Next.js
- With TypeScript
- Architecture
- Core Concepts
- Context-Aware Advertising
- Verified Impression Tracking
- Security & Fraud Prevention
- Configuration
- Provider Configuration
- Component Props
- Impression Tracking
- API Reference
- Components
- Hooks
- Types
- Utilities
- Advanced Usage
- Custom Ad Rendering
- Event Tracking & Analytics
- Custom Styling
- Impression Tracking Deep Dive
- How It Works
- Configuration Options
- Security Features
- Examples
- Troubleshooting
- Performance
- Browser Support
- Contributing
- Support
- License
---
@adsoverai/react is a production-ready SDK that brings native advertising to AI-powered chat interfaces. Built with React and TypeScript, it provides context-aware ads with robust viewability tracking that ensures advertisers only pay for ads that users actually see.
- ๐ฏ Smart Ad Matching - Leverages AI to display relevant ads based on conversation context
- ๐๏ธ IAB-Compliant Tracking - Industry-standard viewability measurement
- ๐ Built-in Fraud Prevention - Cryptographic tokens and runtime integrity checks
- โก Developer Friendly - Simple API, full TypeScript support, comprehensive documentation
- ๐จ Customizable - Flexible theming and custom rendering options
- ๐ฆ Lightweight - Only ~15KB gzipped with intelligent caching
---
- โ
IntersectionObserver-Based - Precise viewport detection with configurable thresholds
- โ
Time-Validated - Ads must remain visible for a minimum duration (default: 1 second)
- โ
Fraud Prevention - Cryptographic impression tokens prevent replay attacks
- โ
DOM Validation - Detects hidden ads (opacity: 0, display: none, etc.)
- โ
Runtime Integrity Checks - Browser environment validation with trust scoring
- โ
Deduplication - Each impression token can only be redeemed once
---
``bashnpm
npm install @adsoverai/react
$3
The SDK requires React 18 or higher:
`json
{
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
`$3
For quick prototyping, demos, or projects without a build system, you can use the SDK directly from a CDN:
#### Option 1: unpkg
`html
`#### Option 2: jsDelivr (Faster Performance)
`html
`#### Option 3: ESM (Modern Browsers)
For modern browsers with ES module support:
`html
`#### CDN Version Pinning
Always pin to a specific version in production to avoid unexpected breaking changes:
`html
`#### CDN with Fallback
For production reliability, use a fallback CDN:
`html
onerror="this.onerror=null; this.src='https://cdn.jsdelivr.net/npm/@adsoverai/react@1.1.3/dist/index.global.js'">
`> Note: When using CDN, your API key will be visible in the browser source. Make sure to:
> - Use a client-side API key (not a server secret)
> - Enable domain restrictions in your AdsOverAI dashboard
> - Monitor usage for unexpected traffic
---
Quick Start
$3
The simplest way to get started with AdsOverAI:
`tsx
import { AdsOverAIProvider, AdsOverAI } from '@adsoverai/react';
import '@adsoverai/react/styles';function App() {
return (
);
}
function ChatInterface() {
const [messages, setMessages] = useState([
{ query: 'best running shoes', response: 'Here are some excellent options...' }
]);
return (
{messages.map((msg, i) => (
{msg.query}
{msg.response}
{/ Display relevant ads /}
query={msg.query}
response={msg.response}
maxAds={3}
theme="auto"
/>
))}
);
}
`$3
#### App Router (Next.js 13+)
`tsx
// app/layout.tsx
import { AdsOverAIProvider } from '@adsoverai/react';
import '@adsoverai/react/styles';export default function RootLayout({ children }) {
return (
apiKey={process.env.NEXT_PUBLIC_ADSOVERAI_KEY}
impressionTracking={{
enabled: true,
viewportThreshold: 0.6,
visibilityDuration: 1500
}}
>
{children}
);
}// app/chat/page.tsx
'use client';
import { AdsOverAI } from '@adsoverai/react';
export default function ChatPage() {
return (
query="travel destinations"
response="Consider these amazing places..."
onAdImpression={(ad, event) => {
console.log('Impression recorded:', ad.ad_id);
console.log('Trust score:', event.client_integrity?.trust_score);
}}
/>
);
}
`#### Pages Router (Next.js 12 and below)
`tsx
// pages/_app.tsx
import { AdsOverAIProvider } from '@adsoverai/react';
import '@adsoverai/react/styles';function MyApp({ Component, pageProps }) {
return (
);
}
export default MyApp;
`$3
Full type safety out of the box:
`typescript
import type {
Ad,
AdsOverAIProps,
AdsOverAIProviderProps,
ImpressionConfig,
ImpressionEvent
} from '@adsoverai/react';const impressionConfig: ImpressionConfig = {
enabled: true,
viewportThreshold: 0.5,
visibilityDuration: 1000
};
const handleImpression = (ad: Ad, event: ImpressionEvent): void => {
console.log(
Impression for ${ad.product_name});
console.log(Trust score: ${event.client_integrity?.trust_score});
};
`---
Architecture
The SDK follows a provider-consumer pattern with intelligent caching and security layers:
`mermaid
graph LR
A[React App] --> B[AdsOverAIProvider]
B --> C[AdsOverAI Component]
C --> D[useAdsOverAI Hook]
D --> E[SWR Cache]
E --> F[AdsOverAI API]
C --> G[AdCard Component]
G --> H[ImpressionWrapper]
H --> I[useAdImpression Hook]
I --> J[IntersectionObserver]
I --> K[DOM Validator]
I --> L[Integrity Checker]
L --> M[Analytics API]
style B fill:#e3f2fd
style F fill:#fff3e0
style M fill:#f3e5f5
`$3
1. Initialization:
AdsOverAIProvider sets up global configuration and context
2. Ad Fetching: useAdsOverAI hook fetches ads via SWR with intelligent caching
3. Rendering: AdCard components display ads with visual themes
4. Tracking: ImpressionWrapper monitors viewport visibility via IntersectionObserver
5. Validation: DOM and integrity checks verify authentic viewing
6. Recording: Verified impressions are sent to analytics API with cryptographic tokens---
Core Concepts
$3
AdsOverAI uses AI-powered matching to display relevant ads based on conversation context:
`tsx
query="best laptop for programming"
response="I recommend laptops with good keyboards and powerful processors..."
/>
`The backend analyzes both the user's query and the AI's response to find the most relevant ads, considering:
- Semantic matching - Understanding intent beyond keywords
- Category relevance - Industry-specific ad targeting
- Confidence scoring - Only high-quality matches are shown
$3
Unlike traditional banner ads, our impression tracking ensures ads are actually seen:
`tsx
impressionTracking={{
viewportThreshold: 0.5, // 50% of ad must be visible
visibilityDuration: 1000 // For at least 1 second
}}
/>
`What counts as an impression?
1. Ad element is at least 50% visible in the viewport (configurable)
2. Remains continuously visible for 1+ seconds (configurable)
3. Not hidden by CSS (opacity, visibility, display)
4. Has non-zero dimensions
5. Passes runtime integrity checks
6. Valid, non-expired impression token
$3
Multi-layered security approach:
`mermaid
graph TD
A[Ad Served] -->|With Token| B[Token Verification]
B --> C[DOM Validation]
C --> D[Geometry Check]
D --> E[Integrity Score]
E --> F{Trust Score > 0.7?}
F -->|Yes| G[Record Impression]
F -->|No| H[Flag as Low Quality]
G --> I[Dedupe Check]
I -->|First Time| J[Success]
I -->|Replay| K[Reject]
`Security Layers:
1. Cryptographic Tokens - Server-signed, single-use tokens
2. DOM Validation - Checks for hidden/zero-size elements
3. Runtime Integrity - Detects tampered browser APIs
4. Trust Scoring - 0.0-1.0 confidence in impression authenticity
5. Deduplication - Prevents replay attacks
6. Rate Limiting - Backend behavioral analysis
---
Configuration
$3
Configure global SDK behavior via
:`tsx
apiKey="your-api-key" // Required: Your API key
maxAds={3} // Default: 3
theme="auto" // 'light' | 'dark' | 'auto'
skeletonVariant="card" // 'card' | 'banner' | 'minimal'
apiUrl="https://api.adsonai.com" // Custom API endpoint
debugMode={false} // Enable console logging
impressionTracking={{
enabled: true,
viewportThreshold: 0.5,
visibilityDuration: 1000
}}
>
{children}
#### Provider Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
|
apiKey | string | Required | Your AdsOverAI API key |
| maxAds | number | 3 | Maximum number of ads to display |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | Theme for ad display |
| skeletonVariant | 'card' \| 'banner' \| 'minimal' | 'card' | Loading skeleton style |
| apiUrl | string | 'https://api.adsonai.com' | API endpoint URL |
| debugMode | boolean | false | Enable debug logging |
| impressionTracking | ImpressionConfig | { enabled: true } | Impression tracking configuration |$3
Override provider defaults per component:
`tsx
query="user search query" // Required
response="AI response text" // Required
maxAds={2} // Override provider default
theme="dark" // Override provider theme
skeletonVariant="banner" // Override skeleton
adPosition="bottom" // 'top' | 'bottom' | 'side'
className="my-ads" // Additional CSS classes
onAdClick={(ad) => track('click', ad)}
onAdLoad={(ads) => console.log(ads)}
onAdImpression={(ad, event) => {
analytics.track('impression', { ad, event })
}}
customAdRenderer={({ ads, ImpressionWrapper }) => (
// Custom rendering logic
)}
/>
`#### AdsOverAI Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
|
query | string | Required | User's search query or message |
| response | string | Required | AI's response text |
| maxAds | number | Provider default | Override max ads |
| theme | 'light' \| 'dark' \| 'auto' | Provider default | Override theme |
| skeletonVariant | 'card' \| 'banner' \| 'minimal' | Provider default | Override skeleton |
| adPosition | 'top' \| 'bottom' \| 'side' | 'bottom' | Position of ads |
| className | string | '' | Additional CSS classes |
| onAdClick | (ad: Ad) => void | undefined | Click event handler |
| onAdLoad | (ads: Ad[]) => void | undefined | Load event handler |
| onAdImpression | (ad: Ad, event: ImpressionEvent) => void | undefined | Impression event handler |
| customAdRenderer | Function | undefined | Custom rendering function |$3
Fine-tune viewability requirements:
`tsx
impressionTracking={{
enabled: true, // Enable/disable tracking
viewportThreshold: 0.75, // 75% of ad must be visible
visibilityDuration: 2000 // Must be visible for 2 seconds
}}
/>
`#### Impression Config Options
| Property | Type | Default | Description |
|----------|------|---------|-------------|
|
enabled | boolean | true | Enable/disable impression tracking |
| viewportThreshold | number (0-1) | 0.5 | Percentage of ad that must be visible |
| visibilityDuration | number (ms) | 1000 | How long ad must remain visible |Recommended Values:
- Standard Display:
{ viewportThreshold: 0.5, visibilityDuration: 1000 }
- Premium Placements: { viewportThreshold: 0.75, visibilityDuration: 2000 }
- Sidebar Ads: { viewportThreshold: 1.0, visibilityDuration: 1500 }---
API Reference
$3
####
Root component that provides SDK configuration via React Context.
`tsx
import { AdsOverAIProvider } from '@adsoverai/react';
`####
Main component for displaying ads alongside AI responses.
`tsx
import { AdsOverAI } from '@adsoverai/react';
`####
Individual ad display component (usually used internally, but exportable for custom layouts).
`tsx
import { AdCard } from '@adsoverai/react';
`####
Wraps ad content to enable impression tracking. Used in custom renderers.
`tsx
import { ImpressionWrapper } from '@adsoverai/react';
`####
Loading placeholder component.
`tsx
import { AdSkeleton } from '@adsoverai/react';
`$3
The SDK includes four distinct ad card variations for different use cases:
####
Minimal design with only essential information - perfect for non-intrusive placements.
Features: Brand name, product name, "Sponsored" label
`tsx
import { AdCardSimple } from '@adsoverai/react'; ad={ad}
theme="light"
apiKey="your-api-key"
apiUrl="https://api.adsoverai.com"
onImpression={(ad, event) => console.log('Impression:', event)}
/>
`####
Call-to-action focused with prominent CTA button - ideal for conversion-focused placements.
Features: Brand, product, description, price, large CTA button
`tsx
import { AdCardCTA } from '@adsoverai/react';
`####
Shows a preview/screenshot of the landing page - great for visual engagement.**
Features: Preview image (customizable size), brand, product, CTA button
`tsx
import { AdCardPreview } from '@adsoverai/react'; ad={ad}
theme="light"
previewConfig={{
width: 400,
height: 200,
showPreview: true
}}
/>
`Preview Image: Provide
preview_image_url in your ad data:
`tsx
const ad = {
// ... other properties
preview_image_url: "https://example.com/preview.jpg"
};
`####
Category-based theming with dynamic gradients and badges.
Features: Category badge, gradient background, category-specific colors
Predefined Themes: Sports & Fitness, Technology, Fashion, Food & Beverage, Travel, General
`tsx
import { AdCardCategory } from '@adsoverai/react';// Automatic theme based on ad.category
// Custom category theme
ad={ad}
theme="light"
categoryTheme={{
gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
textColor: '#FFFFFF',
accentColor: '#FFD700'
}}
/>
`Visual Examples:
Simple Minimal Card
!Simple minimal ad card with clean design
CTA-Focused Card
!CTA-focused ad card with prominent button
Landing Page Preview Card
!Preview ad card with landing page screenshot
Category-Based Card
!Category-based ad card with gradient theming
Using with Custom Renderer:
`tsx
query="running shoes"
response="Looking for the best running shoes..."
customAdRenderer={({ ads, ImpressionWrapper }) => (
{ads.map((ad, index) => (
{index === 0 ? (
) : ad.category === 'Sports & Fitness' ? (
) : (
)}
))}
)}
/>
`> ๐ Complete Documentation: See AD_CARD_VARIATIONS.md for detailed API reference, all category themes, and more examples.
> ๐จ Live Demo: View
examples/ad-card-variations-demo.html to see all variations in action.####
useAdsOverAIFetch ads programmatically with SWR caching.
`tsx
import { useAdsOverAI } from '@adsoverai/react';function MyComponent() {
const { ads, loading, error, refetch } = useAdsOverAI({
query: 'laptop recommendations',
response: 'Here are some options...',
maxAds: 3,
apiKey: 'your-api-key',
apiUrl: 'https://api.adsonai.com',
enabled: true,
});
if (loading) return
Loading ads...;
if (error) return Error: {error.message}; return (
{ads.map(ad => (
{ad.product_name}
))}
);
}
`Returns:
-
ads: Ad[] - Array of ad objects
- loading: boolean - Loading state
- error: Error | undefined - Error object if failed
- refetch: () => void - Manually refetch ads####
useAdsOverAIContextAccess provider context values.
`tsx
import { useAdsOverAIContext } from '@adsoverai/react';function MyComponent() {
const {
apiKey,
theme,
maxAds,
impressionTracking,
debugMode
} = useAdsOverAIContext();
return
Current theme: {theme};
}
`Returns:
-
apiKey: string
- maxAds: number
- theme: 'light' | 'dark' | 'auto'
- skeletonVariant: 'card' | 'banner' | 'minimal'
- apiUrl: string
- debugMode: boolean
- impressionTracking: ImpressionConfig####
useAdImpressionLow-level hook for custom impression tracking (advanced usage).
`tsx
import { useAdImpression } from '@adsoverai/react';function CustomAdComponent({ ad }) {
const adRef = useAdImpression({
ad,
onImpression: (ad, event) => {
console.log('Impression recorded!', ad, event);
},
config: {
enabled: true,
viewportThreshold: 0.5,
visibilityDuration: 1000
}
});
return
Ad content;
}
`$3
####
Ad`typescript
interface Ad {
ad_id: string;
brand_name: string;
product_name: string;
description: string;
ad_text?: string;
cta_text?: string;
cta_link?: string;
landing_url?: string;
category?: string;
price_range?: string;
relevanceScore?: number;
confidence?: number;
matching_metadata?: {
relevance_score: number;
intent_score: number;
semantic_score: number;
confidence_level: number;
ai_reason?: string;
};
impression_token?: string;
}
`####
ImpressionConfig`typescript
interface ImpressionConfig {
enabled: boolean;
viewportThreshold: number; // 0.0 - 1.0
visibilityDuration: number; // milliseconds
}
`####
ImpressionEvent`typescript
interface ImpressionEvent {
ad_id: string;
impression_token: string;
timestamp: string;
viewport_threshold: number;
visibility_duration: number;
screen_dimensions: { width: number; height: number };
user_agent: string;
client_integrity?: {
is_native_observer: boolean;
is_native_timer: boolean;
trust_score: number; // 0.0 - 1.0
};
}
`####
AdsOverAIProviderProps`typescript
interface AdsOverAIProviderProps {
apiKey: string;
maxAds?: number;
theme?: 'light' | 'dark' | 'auto';
skeletonVariant?: 'card' | 'banner' | 'minimal';
apiUrl?: string;
debugMode?: boolean;
impressionTracking?: Partial;
children: React.ReactNode;
}
`####
AdsOverAIProps`typescript
interface AdsOverAIProps {
query: string;
response: string;
maxAds?: number;
theme?: 'light' | 'dark' | 'auto';
skeletonVariant?: 'card' | 'banner' | 'minimal';
adPosition?: 'top' | 'bottom' | 'side';
className?: string;
onAdClick?: (ad: Ad) => void;
onAdLoad?: (ads: Ad[]) => void;
onAdImpression?: (ad: Ad, event: ImpressionEvent) => void;
customAdRenderer?: ((ads: Ad[]) => React.ReactNode) | ((props: {
ads: Ad[];
ImpressionWrapper: React.ComponentType<{ ad: Ad; children: React.ReactNode }>;
}) => React.ReactNode);
}
`$3
####
sendImpressionEventSend impression event to analytics API (used internally, but exportable).
`typescript
import { sendImpressionEvent } from '@adsoverai/react';await sendImpressionEvent(
impressionEvent, // ImpressionEvent object
apiKey, // Your API key
apiUrl // API endpoint URL
);
`---
Advanced Usage
$3
Create fully custom ad layouts while maintaining impression tracking:
`tsx
query={query}
response={response}
customAdRenderer={({ ads, ImpressionWrapper }) => (
{ads.map(ad => (

{ad.brand_name}
{ad.product_name}
{ad.description}
{ad.price_range && {ad.price_range}}
))}
)}
/>
`Important: Always wrap each ad with
to enable tracking! Without it, impressions won't be recorded.#### Using ImpressionWrapper Independently
`tsx
import { ImpressionWrapper, useAdsOverAI } from '@adsoverai/react';function CustomAdDisplay() {
const { ads } = useAdsOverAI({ query: '...', response: '...' });
return (
{ads.map(ad => (
))}
);
}
`#### Legacy Custom Renderer (No Tracking)
The old signature is still supported but won't track impressions:
`tsx
// โ ๏ธ This works but won't track impressions
customAdRenderer={(ads) => (
{ads.map(ad => (
{ad.product_name}
))}
)}
`$3
Track all ad interactions for your analytics platform:
`tsx
query={query}
response={response}
onAdLoad={(ads) => {
// Track ad delivery
analytics.track('ads_loaded', {
count: ads.length,
query: query,
adIds: ads.map(a => a.ad_id)
});
}}
onAdClick={(ad) => {
// Track clicks
analytics.track('ad_clicked', {
adId: ad.ad_id,
brandName: ad.brand_name,
productName: ad.product_name,
ctaLink: ad.cta_link
});
}}
onAdImpression={(ad, event) => {
// Track verified impressions
analytics.track('ad_impression', {
adId: ad.ad_id,
brandName: ad.brand_name,
productName: ad.product_name,
trustScore: event.client_integrity?.trust_score,
viewportThreshold: event.viewport_threshold,
visibilityDuration: event.visibility_duration,
timestamp: event.timestamp,
isNativeObserver: event.client_integrity?.is_native_observer,
screenSize: event.screen_dimensions
});
// Optional: Send to your own backend
fetch('/api/track-impression', {
method: 'POST',
body: JSON.stringify({ ad, event })
});
}}
/>
`$3
Override default styles using CSS variables:
`css
/ Light theme customization /
.adsoverai-message {
--adsoverai-bg: #f9f9f9;
--adsoverai-text: #333;
--adsoverai-border: #ddd;
--adsoverai-border-radius: 12px;
--adsoverai-shadow: 0 2px 8px rgba(0,0,0,0.1);
}/ Dark theme customization /
[data-theme='dark'] .adsoverai-message {
--adsoverai-bg: #1a1a1a;
--adsoverai-text: #ffffff;
--adsoverai-border: #404040;
--adsoverai-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
/ Button customization /
.adsoverai-cta {
--adsoverai-button-bg: #007bff;
--adsoverai-button-hover: #0056b3;
--adsoverai-button-text: #ffffff;
}
/ Custom ad container /
.my-custom-ads .adsoverai-message {
padding: 24px;
margin: 16px 0;
}
`#### Available CSS Variables
-
--adsoverai-bg - Background color
- --adsoverai-text - Text color
- --adsoverai-border - Border color
- --adsoverai-border-radius - Border radius
- --adsoverai-shadow - Box shadow
- --adsoverai-button-bg - CTA button background
- --adsoverai-button-hover - CTA button hover state
- --adsoverai-button-text - CTA button text color---
Impression Tracking Deep Dive
$3
Our impression tracking ensures advertisers only pay for ads that users actually see:
`mermaid
graph TD
A[Ad Renders] -->|Off Screen| B(Idle State)
B -->|User Scrolls| C{50% Visible?}
C -->|No| B
C -->|Yes| D[Start Timer]
D -->|Wait 1s| E{Still Visible?}
E -->|No| F[Cancel Timer]
F --> B
E -->|Yes| G[Validate DOM State]
G -->|Check opacity, visibility, display| H[Check Element Geometry]
H -->|Non-zero width/height| I[Runtime Integrity Check]
I -->|Verify native APIs| J[Calculate Trust Score]
J -->|Score 0.0-1.0| K[Fire Impression Event]
K -->|Send with Token| L[Backend Verification]
L -->|Verify signature| M{Valid Token?}
M -->|Yes| N[Check Deduplication]
N -->|First Use| O[Record Impression โ
]
N -->|Replay| P[Reject - Already Used โ]
M -->|Invalid/Expired| Q[Reject - Bad Token โ]
`$3
1. Ad Renders: Component mounts and IntersectionObserver is initialized
2. Viewport Detection: Observer detects when ad enters viewport
3. Threshold Check: Verifies if configured percentage is visible (default 50%)
4. Timer Start: Starts countdown for visibility duration (default 1000ms)
5. Continuous Monitoring: Checks if ad stays visible during duration
6. DOM Validation: Before recording, validates:
- Element has
opacity > 0
- Element has visibility !== 'hidden'
- Element has display !== 'none'
- Element has non-zero width and height
- Element is attached to document.body
7. Integrity Check: Verifies browser environment:
- IntersectionObserver is native (not mocked)
- setTimeout is native (not tampered)
- Not running in webdriver/bot
- HTTPS secure context
8. Trust Score: Calculates 0.0-1.0 confidence score
9. Event Emission: Fires onAdImpression callback
10. API Request: Sends impression event with cryptographic token
11. Backend Verification: Server verifies token signature and validity
12. Deduplication: Ensures token hasn't been used before
13. Recording: Impression is recorded (or rejected if invalid)$3
Configure viewability requirements globally:
`tsx
impressionTracking={{
enabled: true, // Master switch
viewportThreshold: 0.5, // 50% visible (0.0 - 1.0)
visibilityDuration: 1000 // 1 second (milliseconds)
}}
/>
`#### Configuration Guidelines
| Use Case | Threshold | Duration | Rationale |
|----------|-----------|----------|-----------|
| Standard Display Ads | 0.5 | 1000ms | IAB standard for display advertising |
| Premium Placements | 0.75 | 2000ms | Higher confidence of user attention |
| Sidebar/Persistent Ads | 1.0 | 1500ms | Ensure complete visibility |
| Mobile/Fast Scroll | 0.5 | 500ms | Adjust for quick scrolling patterns |
| Video Ads | 0.5 | 2000ms | Longer engagement requirement |
$3
`tsx
query={query}
response={response}
onAdImpression={(ad, event) => {
console.log('โ
Valid impression recorded');
console.log('Ad:', ad.product_name);
console.log('Trust score:', event.client_integrity?.trust_score);
console.log('Viewport threshold met:', event.viewport_threshold);
console.log('Visibility duration:', event.visibility_duration);
console.log('Screen size:', event.screen_dimensions);
console.log('Native observer:', event.client_integrity?.is_native_observer);
console.log('Timestamp:', event.timestamp);
// Send to your analytics
analytics.track('ad_impression', {
adId: ad.ad_id,
trustScore: event.client_integrity?.trust_score,
viewabilityMet: event.viewport_threshold >= 0.5
});
}}
/>
`$3
#### 1. Cryptographic Tokens
Each ad includes a server-signed
impression_token:`json
{
"ad_id": "ad_12345",
"impression_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
`The token encodes:
-
ad_id - Which ad was served
- application_id - Which app requested it
- issued_at - When token was created
- expires_at - Token expiration time
- signature - Cryptographic signatureBackend verifies the signature before recording any impression.
#### 2. DOM Validation
Before recording impression, we check:
`javascript
// Pseudo-code of validation checks
const computedStyle = window.getComputedStyle(element);
const rect = element.getBoundingClientRect();const isValid =
computedStyle.opacity !== '0' &&
computedStyle.visibility !== 'hidden' &&
computedStyle.display !== 'none' &&
rect.width > 0 &&
rect.height > 0 &&
document.body.contains(element);
`This prevents "hidden ad fraud" where ads are loaded but not actually visible.
#### 3. Runtime Integrity Checks
We detect tampered browser environments:
`javascript
// Check if IntersectionObserver is native
const isNativeObserver =
IntersectionObserver.toString().includes('[native code]');// Check if setTimeout is native
const isNativeTimer =
setTimeout.toString().includes('[native code]');
// Detect webdriver/automation
const isWebdriver = navigator.webdriver === true;
// Verify HTTPS context
const isSecureContext = window.isSecureContext === true;
// Calculate trust score (0.0 - 1.0)
const trustScore = calculateTrustScore({
isNativeObserver,
isNativeTimer,
isWebdriver,
isSecureContext
});
`Trust scores are sent with each impression and used by the backend for quality filtering.
#### 4. Deduplication
Each
impression_token can only be used once:`
Client sends: impression_token = "abc123..."
Server checks: Has "abc123..." been used?
โ NO: Record impression โ
โ YES: Reject as replay attack โ
`#### 5. Behavioral Analysis (Backend)
The backend analyzes patterns:
- Rate limiting - Too many impressions from one IP/device
- Timing patterns - Suspiciously consistent visibility durations
- Trust score filtering - Low trust scores flagged for review
- Geographic validation - Cross-reference with expected user locations
---
Examples
$3
`tsx
import { useState } from 'react';
import { AdsOverAIProvider, AdsOverAI } from '@adsoverai/react';
import '@adsoverai/react/styles';function ChatApp() {
const [messages, setMessages] = useState([
{
id: 1,
query: 'best gaming laptops',
response: 'Here are top picks for gaming laptops in 2024...'
}
]);
return (
apiKey="your-api-key"
theme="auto"
impressionTracking={{
enabled: true,
viewportThreshold: 0.6,
visibilityDuration: 1500
}}
>
{messages.map((msg) => (
{msg.query}
{msg.response}
query={msg.query}
response={msg.response}
maxAds={3}
onAdImpression={(ad, event) => {
console.log(Impression: ${ad.product_name});
console.log(Trust: ${event.client_integrity?.trust_score});
}}
/>
))}
$3
`tsx
import { AdsOverAI } from '@adsoverai/react';function SearchResults({ query, results }) {
const resultsText = results
.map(r =>
${r.name}: ${r.description})
.join('. '); return (
Search Results for "{query}"
{/ Display search results /}
{results.map(result => (
))}
{/ Display relevant ads /}
query={query}
response={resultsText}
maxAds={4}
adPosition="side"
theme="light"
/>
);
}
`$3
`tsx
import { AdsOverAI } from '@adsoverai/react';function SupportChat({ conversation }) {
return (
{conversation.map((turn, idx) => (
{turn.customerMessage}
{turn.botResponse}
{/ Show ads for product recommendations /}
{turn.intent === 'product_inquiry' && (
query={turn.customerMessage}
response={turn.botResponse}
maxAds={2}
theme="auto"
onAdClick={(ad) => {
// Track click-through from support chat
analytics.track('support_ad_click', {
conversationId: conversation.id,
adId: ad.ad_id,
intent: turn.intent
});
}}
/>
)}
))}
);
}
`$3
`tsx
import { AdsOverAI } from '@adsoverai/react';function MultiLangChat({ language, query, response }) {
return (
query={query}
response={response}
// Ads will be matched based on content language
onAdLoad={(ads) => {
console.log(
Loaded ${ads.length} ads for ${language});
}}
/>
);
}
`$3
`tsx
import { AdsOverAI } from '@adsoverai/react';function AnalyticsExample() {
const trackEvent = (eventName, properties) => {
// Send to your analytics platform
window.analytics.track(eventName, properties);
// Send to your backend
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify({ event: eventName, ...properties })
});
};
return (
query="best CRM software"
response="Here are some popular CRM solutions..."
onAdLoad={(ads) => {
trackEvent('ads_rendered', {
count: ads.length,
adIds: ads.map(a => a.ad_id),
brands: ads.map(a => a.brand_name)
});
}}
onAdClick={(ad) => {
trackEvent('ad_clicked', {
adId: ad.ad_id,
brand: ad.brand_name,
product: ad.product_name,
ctaText: ad.cta_text
});
}}
onAdImpression={(ad, event) => {
trackEvent('ad_impression', {
adId: ad.ad_id,
brand: ad.brand_name,
trustScore: event.client_integrity?.trust_score,
viewportThreshold: event.viewport_threshold,
visibilityMs: event.visibility_duration,
isNative: event.client_integrity?.is_native_observer
});
}}
/>
);
}
`---
Troubleshooting
$3
Problem: No ads are displayed, only skeleton or nothing.
Solutions:
1. Verify API Key
`tsx
// Enable debug mode to see console logs
`2. Check Network Requests
- Open DevTools โ Network tab
- Look for requests to your API endpoint
- Check response status and body
3. Verify Query Content
`tsx
// Query should have meaningful content
query="laptop" // โ Too short
query="I need a laptop for video editing" // โ
Good
/>
`4. Check for Errors
`tsx
const { ads, error } = useAdsOverAI({ ... });
if (error) console.error('Ad fetch error:', error);
`$3
Problem:
onAdImpression callback never fires.Solutions:
1. Using Custom Renderer? Wrap with ImpressionWrapper!
`tsx
// โ Wrong - no impression tracking
customAdRenderer={(ads) => (
{ads.map(ad => {ad.product_name})}
)}
// โ
Correct - with ImpressionWrapper
customAdRenderer={({ ads, ImpressionWrapper }) => (
{ads.map(ad => (
{ad.product_name}
))}
)}
`2. Verify Impression Tracking is Enabled
`tsx
impressionTracking={{ enabled: true }} // Make sure this is true
/>
`3. Check Console Logs
`
[AdsOverAI] Recording impression for ad: ad_12345
โ
Impression sent to backend: imp_67890
`
If you see:
`
[AdsOverAI] Skipping impression - missing required data
`
Check that ads have impression_token from backend.4. Verify Ads Are Actually Visible
- Not hidden by
display: none, opacity: 0, visibility: hidden
- Have non-zero width and height
- Are scrolled into viewport
- Stay visible for required duration (default 1 second)5. Check Provider Configuration
`tsx
apiKey="your-api-key" // โ Required
apiUrl="https://www.adsoverai.com" // โ Required
impressionTracking={{
enabled: true,
viewportThreshold: 0.5,
visibilityDuration: 1000
}}
/>
`$3
Problem: Type errors when using the SDK.
Solutions:
1. Install Type Definitions
`bash
npm install --save-dev @types/react @types/react-dom
`2. Verify TypeScript Configuration
`json
// tsconfig.json
{
"compilerOptions": {
"jsx": "react-jsx",
"esModuleInterop": true,
"skipLibCheck": true
}
}
`3. Import Types Explicitly
`typescript
import type { Ad, ImpressionEvent } from '@adsoverai/react';
`$3
Problem: SDK styles conflict with your app's CSS.
Solutions:
1. Import SDK Styles Last
`tsx
import './app.css';
import '@adsoverai/react/styles'; // โ Import after your CSS
`2. Use CSS Specificity
`css
/ Your overrides /
.my-app .adsoverai-message {
background: custom-color;
}
`3. CSS Variables
`css
.adsoverai-message {
--adsoverai-bg: your-color;
}
`$3
Problem: Errors during Next.js build.
Solutions:
1. Mark Component as Client-Side
`tsx
'use client'; // โ Add this at top of file
import { AdsOverAI } from '@adsoverai/react';
`2. Dynamic Import (Pages Router)
`tsx
import dynamic from 'next/dynamic';
const AdsOverAI = dynamic(
() => import('@adsoverai/react').then(mod => mod.AdsOverAI),
{ ssr: false }
);
`$3
Problem: Ads loading slowly or causing lag.
Solutions:
1. Leverage SWR Caching
`tsx
// Ads are cached for 5 minutes by default
// Subsequent renders with same query are instant
`2. Limit Max Ads
`tsx
// Show fewer ads
`3. Use Skeleton Variants Wisely
`tsx
// Minimal skeleton for faster perceived load
`4. Optimize Custom Renderers
`tsx
// Avoid heavy computations in custom renderer
customAdRenderer={React.memo(({ ads, ImpressionWrapper }) => (
// Memoized rendering logic
))}
`---
Performance
$3
- Core SDK: ~15KB gzipped
- Styles: ~3KB gzipped
- Total: ~18KB gzipped
The SDK is tree-shakable - import only what you need:
`tsx
// Import specific components
import { AdsOverAI } from '@adsoverai/react'; // Smaller bundle// vs importing everything
import * as AdsOverAI from '@adsoverai/react'; // Larger bundle
`$3
SWR-Powered Intelligent Caching:
`tsx
// First render - fetches from API
// Second render with same query - instant from cache
// Cache expires after 5 minutes, then auto-revalidates
`Benefits:
- โก Zero-latency for cached queries
- ๐ Automatic background revalidation
- ๐ฏ Request deduplication (multiple components = one request)
- ๐พ Reduced API calls = lower costs
$3
Skeleton Loaders:
- Prevent layout shift
- Maintain UI space while loading
- Smooth transition to actual ads
Lazy Intersection Observer:
- Only tracks ads when near viewport
- Efficient passive event listeners
- Automatic cleanup on unmount
CSS Performance:
- Hardware-accelerated animations
- Minimal repaints
- Optimized pseudo-selectors
---
Browser Support
$3
- โ
Chrome/Edge: Latest 2 versions
- โ
Firefox: Latest 2 versions
- โ
Safari: Latest 2 versions (including iOS Safari)
- โ
Samsung Internet: Latest version
- โ
Chrome Android: Latest 2 versions
$3
The SDK requires modern browser APIs:
- IntersectionObserver - For viewport detection (available in all modern browsers)
- Promises - For async operations
- fetch - For API requests
- Web Crypto API - For security features (optional, degrades gracefully)
$3
If you need to support older browsers:
`bash
npm install intersection-observer
``tsx
// In your app entry point
import 'intersection-observer';
import { AdsOverAIProvider } from '@adsoverai/react';
`---
Contributing
We welcome contributions! Here's how you can help:
$3
`bash
Clone the repository
git clone https://github.com/adsoverai/react-sdk.git
cd react-sdkInstall dependencies
npm installRun development build
npm run devRun tests
npm testRun tests with UI
npm run test:uiType checking
npm run typecheckLinting
npm run lint
npm run lint:fixFormat code
npm run format
`$3
`
src/
โโโ components/ # React components
โ โโโ AdsOverAI.tsx # Main ad display component
โ โโโ AdsOverAIProvider.tsx # Context provider
โ โโโ AdCard.tsx # Individual ad card
โ โโโ ImpressionWrapper.tsx # Impression tracking wrapper
โ โโโ AdSkeleton.tsx # Loading skeletons
โโโ hooks/ # Custom React hooks
โ โโโ useAdsOverAI.ts # Ad fetching hook
โ โโโ useAdImpression.ts # Impression tracking hook
โโโ utils/ # Utility functions
โ โโโ analytics.ts # Analytics/impression API
โ โโโ security.ts # Security & integrity checks
โ โโโ validation.ts # DOM validation
โโโ types/ # TypeScript types
โ โโโ index.ts # Type definitions
โโโ styles/ # CSS styles
โ โโโ index.css # Component styles
โโโ index.ts # Main entry point
`$3
1. Code Style: We use Biome for linting and formatting
2. Tests: Add tests for new features
3. Types: Maintain full TypeScript coverage
4. Documentation: Update README and JSDoc comments
5. Commits: Use conventional commit messages
$3
1. Fork the repository
2. Create a feature branch (
git checkout -b feature/amazing-feature)
3. Make your changes
4. Run tests and linting (npm run test && npm run lint)
5. Commit your changes (git commit -m 'feat: add amazing feature')
6. Push to your fork (git push origin feature/amazing-feature)
7. Open a Pull Request---
Documentation
$3
- ๐ Impression Tracking Deep Dive - Detailed architecture and security
- ๐ SDK Architecture Flow - Data flow diagrams
- ๐ Changelog - Version history and updates
- ๐ Official Website - Product information
- ๐ Full Documentation - Complete guides
$3
-
@adsoverai/vue - Vue.js SDK (coming soon)
- @adsoverai/angular - Angular SDK (coming soon)
- @adsoverai/vanilla` - Vanilla JavaScript SDK (coming soon)---
Need help? We're here for you:
- ๐ง Email: hello@adsoverai.com
- ๐ Website: https://www.adsoverai.com
- ๐ Documentation: https://docs.adsoverai.com
- ๐ Bug Reports: GitHub Issues
- ๐ฌ Discussions: GitHub Discussions
For enterprise support, custom integrations, or priority bug fixes:
- Email: enterprise@adsoverai.com
- Website: https://www.adsoverai.com/enterprise
---
MIT ยฉ AdsOverAI Team
See LICENSE for more information.
---
Built with:
- โ๏ธ React
- ๐ TypeScript
- ๐จ CSS3
- ๐ฆ tsup
- ๐งช Vitest
- ๐ SWR
Special thanks to all our contributors and the open-source community!
---
Made with โค๏ธ by the AdsOverAI Team
โญ Star us on GitHub โข ๐ฆ Follow us on Twitter โข ๐ฐ Read our Blog