npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@adsoverai/react

v1.2.2

Published

React SDK for AdsOverAI - Native advertising for AI chat interfaces

Readme

@adsoverai/react

Native Advertising SDK for AI Chat Interfaces

npm version License: MIT TypeScript Website

Quick StartDocumentationExamplesAPI ReferenceSupport


Table of Contents


Overview

@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.

Why AdsOverAI?

  • 🎯 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

Features

🎯 Context-Aware Ads

Display relevant ads based on AI conversation context. Our backend intelligently matches ads to user queries and AI responses for maximum relevance.

👁️ Verified Impression Tracking

IAB-compliant viewability tracking with fraud prevention. Unlike traditional ad SDKs, we ensure advertisers only pay for ads that users actually see:

  • 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

🔒 Security-First Architecture

  • Cryptographic tokens for impression verification
  • Runtime integrity checks detect tampered browser APIs
  • HTTPS-only secure context enforcement
  • Trust scoring for each impression (0.0-1.0)
  • Backend rate limiting and behavioral analysis

⚡ Performance Optimized

  • 5-minute SWR cache with intelligent deduplication
  • ~15KB minified + gzipped bundle size
  • Tree-shakable exports - import only what you need
  • No layout shift - skeleton loaders maintain ad space
  • Passive event listeners and efficient observers

🎨 Flexible UI & Theming

  • Multiple built-in themes (light, dark, auto)
  • Customizable skeleton variants (card, banner, minimal)
  • CSS variable-based styling system
  • Full support for custom ad renderers
  • WCAG-compliant accessibility
  • Mobile-first responsive design

🔧 Developer Experience

  • Full TypeScript support with comprehensive type definitions
  • React 18+ compatible
  • Simple, intuitive API
  • Comprehensive documentation
  • Debug mode for development
  • Built-in error handling

Installation

# npm
npm install @adsoverai/react

# pnpm
pnpm add @adsoverai/react

# yarn
yarn add @adsoverai/react

Peer Dependencies

The SDK requires React 18 or higher:

{
  "react": ">=18.0.0",
  "react-dom": ">=18.0.0"
}

CDN Installation (No Build Step Required)

For quick prototyping, demos, or projects without a build system, you can use the SDK directly from a CDN:

Option 1: unpkg

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/@adsoverai/[email protected]/dist/styles.css">
</head>
<body>
  <div id="root"></div>

  <!-- Load React -->
  <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
  
  <!-- Load AdsOverAI SDK -->
  <script src="https://unpkg.com/@adsoverai/[email protected]/dist/index.global.js"></script>

  <script>
    const { AdsOverAIProvider, AdsOverAI } = window.AdsOverAI;
    
    const App = () => (
      React.createElement(AdsOverAIProvider, { apiKey: 'your-api-key' },
        React.createElement(AdsOverAI, {
          query: 'best laptop for programming',
          response: 'I recommend laptops with good keyboards and powerful processors...'
        })
      )
    );

    ReactDOM.createRoot(document.getElementById('root')).render(
      React.createElement(App)
    );
  </script>
</body>
</html>

Option 2: jsDelivr (Faster Performance)

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@adsoverai/[email protected]/dist/styles.css">
</head>
<body>
  <div id="root"></div>

  <script crossorigin src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>
  <script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/@adsoverai/[email protected]/dist/index.global.js"></script>

  <script>
    const { AdsOverAIProvider, AdsOverAI } = window.AdsOverAI;
    
    const App = () => (
      React.createElement(AdsOverAIProvider, { apiKey: 'your-api-key' },
        React.createElement(AdsOverAI, {
          query: 'best laptop for programming',
          response: 'I recommend laptops with good keyboards...'
        })
      )
    );

    ReactDOM.createRoot(document.getElementById('root')).render(
      React.createElement(App)
    );
  </script>
</body>
</html>

Option 3: ESM (Modern Browsers)

For modern browsers with ES module support:

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://unpkg.com/@adsoverai/[email protected]/dist/styles.css">
</head>
<body>
  <div id="root"></div>

  <script type="importmap">
    {
      "imports": {
        "react": "https://esm.sh/react@18",
        "react-dom": "https://esm.sh/react-dom@18",
        "react-dom/client": "https://esm.sh/react-dom@18/client"
      }
    }
  </script>

  <script type="module">
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import { AdsOverAIProvider, AdsOverAI } from 'https://unpkg.com/@adsoverai/[email protected]/dist/index.mjs';

    const App = () => (
      React.createElement(AdsOverAIProvider, { apiKey: 'your-api-key' },
        React.createElement(AdsOverAI, {
          query: 'best laptop for programming',
          response: 'I recommend laptops with good keyboards and powerful processors...'
        })
      )
    );

    ReactDOM.createRoot(document.getElementById('root')).render(React.createElement(App));
  </script>
</body>
</html>

CDN Version Pinning

Always pin to a specific version in production to avoid unexpected breaking changes:

<!-- ✅ Recommended: Pinned to exact version -->
<script src="https://unpkg.com/@adsoverai/[email protected]/dist/index.global.js"></script>

<!-- ⚠️ Use with caution: Latest patch version -->
<script src="https://unpkg.com/@adsoverai/[email protected]/dist/index.global.js"></script>

<!-- ❌ Not recommended: Always latest (may include breaking changes) -->
<script src="https://unpkg.com/@adsoverai/react/dist/index.global.js"></script>

CDN with Fallback

For production reliability, use a fallback CDN:

<script src="https://unpkg.com/@adsoverai/[email protected]/dist/index.global.js" 
        onerror="this.onerror=null; this.src='https://cdn.jsdelivr.net/npm/@adsoverai/[email protected]/dist/index.global.js'">
</script>

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

Basic Integration

The simplest way to get started with AdsOverAI:

import { AdsOverAIProvider, AdsOverAI } from '@adsoverai/react';
import '@adsoverai/react/styles';

function App() {
  return (
    <AdsOverAIProvider apiKey="your-api-key">
      <ChatInterface />
    </AdsOverAIProvider>
  );
}

function ChatInterface() {
  const [messages, setMessages] = useState([
    { query: 'best running shoes', response: 'Here are some excellent options...' }
  ]);

  return (
    <div className="chat-container">
      {messages.map((msg, i) => (
        <div key={i}>
          <div className="user-message">{msg.query}</div>
          <div className="ai-response">{msg.response}</div>
          
          {/* Display relevant ads */}
          <AdsOverAI
            query={msg.query}
            response={msg.response}
            maxAds={3}
            theme="auto"
          />
        </div>
      ))}
    </div>
  );
}

With Next.js

App Router (Next.js 13+)

// app/layout.tsx
import { AdsOverAIProvider } from '@adsoverai/react';
import '@adsoverai/react/styles';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <AdsOverAIProvider 
          apiKey={process.env.NEXT_PUBLIC_ADSOVERAI_KEY}
          impressionTracking={{
            enabled: true,
            viewportThreshold: 0.6,
            visibilityDuration: 1500
          }}
        >
          {children}
        </AdsOverAIProvider>
      </body>
    </html>
  );
}

// app/chat/page.tsx
'use client';
import { AdsOverAI } from '@adsoverai/react';

export default function ChatPage() {
  return (
    <AdsOverAI
      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)

// pages/_app.tsx
import { AdsOverAIProvider } from '@adsoverai/react';
import '@adsoverai/react/styles';

function MyApp({ Component, pageProps }) {
  return (
    <AdsOverAIProvider apiKey={process.env.NEXT_PUBLIC_ADSOVERAI_KEY}>
      <Component {...pageProps} />
    </AdsOverAIProvider>
  );
}

export default MyApp;

With TypeScript

Full type safety out of the box:

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:

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

Data Flow

  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

Context-Aware Advertising

AdsOverAI uses AI-powered matching to display relevant ads based on conversation context:

<AdsOverAI
  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

Verified Impression Tracking

Unlike traditional banner ads, our impression tracking ensures ads are actually seen:

<AdsOverAIProvider
  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

Security & Fraud Prevention

Multi-layered security approach:

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

Provider Configuration

Configure global SDK behavior via <AdsOverAIProvider>:

<AdsOverAIProvider
  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}
</AdsOverAIProvider>

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 |

Component Props

Override provider defaults per component:

<AdsOverAI
  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 |

Impression Tracking Configuration

Fine-tune viewability requirements:

<AdsOverAIProvider
  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

Components

<AdsOverAIProvider>

Root component that provides SDK configuration via React Context.

import { AdsOverAIProvider } from '@adsoverai/react';

<AdsOverAIProvider apiKey="your-key" theme="dark">
  <App />
</AdsOverAIProvider>

See Provider Props

<AdsOverAI>

Main component for displaying ads alongside AI responses.

import { AdsOverAI } from '@adsoverai/react';

<AdsOverAI query="user query" response="AI response" />

See Component Props

<AdCard>

Individual ad display component (usually used internally, but exportable for custom layouts).

import { AdCard } from '@adsoverai/react';

<AdCard ad={adObject} theme="light" onAdClick={handleClick} />

<ImpressionWrapper>

Wraps ad content to enable impression tracking. Used in custom renderers.

import { ImpressionWrapper } from '@adsoverai/react';

<ImpressionWrapper ad={ad}>
  <CustomAdComponent ad={ad} />
</ImpressionWrapper>

<AdSkeleton>

Loading placeholder component.

import { AdSkeleton } from '@adsoverai/react';

<AdSkeleton variant="card" count={3} />

Ad Card Variations

The SDK includes four distinct ad card variations for different use cases:

<AdCardSimple>

Minimal design with only essential information - perfect for non-intrusive placements.

Features: Brand name, product name, "Sponsored" label

import { AdCardSimple } from '@adsoverai/react';

<AdCardSimple
  ad={ad}
  theme="light"
  apiKey="your-api-key"
  apiUrl="https://api.adsoverai.com"
  onImpression={(ad, event) => console.log('Impression:', event)}
/>

<AdCardCTA>

Call-to-action focused with prominent CTA button - ideal for conversion-focused placements.

Features: Brand, product, description, price, large CTA button

import { AdCardCTA } from '@adsoverai/react';

<AdCardCTA ad={ad} theme="light" />

<AdCardPreview>

Shows a preview/screenshot of the landing page - great for visual engagement.**

Features: Preview image (customizable size), brand, product, CTA button

import { AdCardPreview } from '@adsoverai/react';

<AdCardPreview
  ad={ad}
  theme="light"
  previewConfig={{
    width: 400,
    height: 200,
    showPreview: true
  }}
/>

Preview Image: Provide preview_image_url in your ad data:

const ad = {
  // ... other properties
  preview_image_url: "https://example.com/preview.jpg"
};

<AdCardCategory>

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

import { AdCardCategory } from '@adsoverai/react';

// Automatic theme based on ad.category
<AdCardCategory ad={ad} theme="light" />

// Custom category theme
<AdCardCategory
  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:

<AdsOverAI
  query="running shoes"
  response="Looking for the best running shoes..."
  customAdRenderer={({ ads, ImpressionWrapper }) => (
    <div style={{ display: 'grid', gap: '20px' }}>
      {ads.map((ad, index) => (
        <ImpressionWrapper key={ad.ad_id} ad={ad}>
          {index === 0 ? (
            <AdCardCTA ad={ad} theme="light" />
          ) : ad.category === 'Sports & Fitness' ? (
            <AdCardCategory ad={ad} theme="light" />
          ) : (
            <AdCardSimple ad={ad} theme="light" />
          )}
        </ImpressionWrapper>
      ))}
    </div>
  )}
/>

📚 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.

useAdsOverAI

Fetch ads programmatically with SWR caching.

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 <div>Loading ads...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      {ads.map(ad => (
        <div key={ad.ad_id}>{ad.product_name}</div>
      ))}
      <button onClick={refetch}>Refresh Ads</button>
    </div>
  );
}

Returns:

  • ads: Ad[] - Array of ad objects
  • loading: boolean - Loading state
  • error: Error | undefined - Error object if failed
  • refetch: () => void - Manually refetch ads

useAdsOverAIContext

Access provider context values.

import { useAdsOverAIContext } from '@adsoverai/react';

function MyComponent() {
  const { 
    apiKey, 
    theme, 
    maxAds, 
    impressionTracking,
    debugMode 
  } = useAdsOverAIContext();
  
  return <div>Current theme: {theme}</div>;
}

Returns:

  • apiKey: string
  • maxAds: number
  • theme: 'light' | 'dark' | 'auto'
  • skeletonVariant: 'card' | 'banner' | 'minimal'
  • apiUrl: string
  • debugMode: boolean
  • impressionTracking: ImpressionConfig

useAdImpression

Low-level hook for custom impression tracking (advanced usage).

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 <div ref={adRef}>Ad content</div>;
}

TypeScript Types

Ad

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

interface ImpressionConfig {
  enabled: boolean;
  viewportThreshold: number;      // 0.0 - 1.0
  visibilityDuration: number;     // milliseconds
}

ImpressionEvent

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

interface AdsOverAIProviderProps {
  apiKey: string;
  maxAds?: number;
  theme?: 'light' | 'dark' | 'auto';
  skeletonVariant?: 'card' | 'banner' | 'minimal';
  apiUrl?: string;
  debugMode?: boolean;
  impressionTracking?: Partial<ImpressionConfig>;
  children: React.ReactNode;
}

AdsOverAIProps

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);
}

Utilities

sendImpressionEvent

Send impression event to analytics API (used internally, but exportable).

import { sendImpressionEvent } from '@adsoverai/react';

await sendImpressionEvent(
  impressionEvent,  // ImpressionEvent object
  apiKey,           // Your API key
  apiUrl            // API endpoint URL
);

Advanced Usage

Custom Ad Rendering

Create fully custom ad layouts while maintaining impression tracking:

<AdsOverAI
  query={query}
  response={response}
  customAdRenderer={({ ads, ImpressionWrapper }) => (
    <div className="my-custom-grid">
      {ads.map(ad => (
        <ImpressionWrapper key={ad.ad_id} ad={ad}>
          <article className="custom-ad-card">
            <header>
              <img src={ad.brand_logo} alt={ad.brand_name} />
              <h3>{ad.brand_name}</h3>
            </header>
            
            <div className="content">
              <h4>{ad.product_name}</h4>
              <p>{ad.description}</p>
              {ad.price_range && <span className="price">{ad.price_range}</span>}
            </div>
            
            <footer>
              <a 
                href={ad.cta_link} 
                target="_blank" 
                rel="noopener noreferrer"
                className="cta-button"
              >
                {ad.cta_text || 'Learn More'}
              </a>
            </footer>
          </article>
        </ImpressionWrapper>
      ))}
    </div>
  )}
/>

Important: Always wrap each ad with <ImpressionWrapper> to enable tracking! Without it, impressions won't be recorded.

Using ImpressionWrapper Independently

import { ImpressionWrapper, useAdsOverAI } from '@adsoverai/react';

function CustomAdDisplay() {
  const { ads } = useAdsOverAI({ query: '...', response: '...' });
  
  return (
    <div className="ad-grid">
      {ads.map(ad => (
        <ImpressionWrapper key={ad.ad_id} ad={ad}>
          <YourCustomAdComponent ad={ad} />
        </ImpressionWrapper>
      ))}
    </div>
  );
}

Legacy Custom Renderer (No Tracking)

The old signature is still supported but won't track impressions:

// ⚠️ This works but won't track impressions
customAdRenderer={(ads) => (
  <div>
    {ads.map(ad => (
      <div key={ad.ad_id}>{ad.product_name}</div>
    ))}
  </div>
)}

Event Tracking & Analytics

Track all ad interactions for your analytics platform:

<AdsOverAI
  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 })
    });
  }}
/>

Custom Styling

Override default styles using CSS variables:

/* 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

How Impression Tracking Works

Our impression tracking ensures advertisers only pay for ads that users actually see:

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 ❌]

Impression Flow Step-by-Step

  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)

Impression Tracking Options

Configure viewability requirements globally:

<AdsOverAIProvider
  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 |

Listening to Impression Events

<AdsOverAI
  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
    });
  }}
/>

Security Features

1. Cryptographic Tokens

Each ad includes a server-signed impression_token:

{
  "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 signature

Backend verifies the signature before recording any impression.

2. DOM Validation

Before recording impression, we check:

// 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:

// 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

Basic Chat Integration

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 (
    <AdsOverAIProvider 
      apiKey="your-api-key" 
      theme="auto"
      impressionTracking={{
        enabled: true,
        viewportThreshold: 0.6,
        visibilityDuration: 1500
      }}
    >
      <div className="chat-container">
        {messages.map((msg) => (
          <div key={msg.id} className="message-group">
            <div className="user-query">{msg.query}</div>
            <div className="ai-response">{msg.response}</div>
            
            <AdsOverAI
              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}`);
              }}
            />
          </div>
        ))}
      </div>
    </AdsOverAIProvider>
  );
}

E-commerce Search Integration

import { AdsOverAI } from '@adsoverai/react';

function SearchResults({ query, results }) {
  const resultsText = results
    .map(r => `${r.name}: ${r.description}`)
    .join('. ');

  return (
    <div className="search-page">
      <h1>Search Results for "{query}"</h1>
      
      {/* Display search results */}
      <div className="results-grid">
        {results.map(result => (
          <ProductCard key={result.id} product={result} />
        ))}
      </div>
      
      {/* Display relevant ads */}
      <AdsOverAI
        query={query}
        response={resultsText}
        maxAds={4}
        adPosition="side"
        theme="light"
      />
    </div>
  );
}

Customer Support Bot

import { AdsOverAI } from '@adsoverai/react';

function SupportChat({ conversation }) {
  return (
    <div className="support-chat">
      {conversation.map((turn, idx) => (
        <div key={idx}>
          <div className="customer-message">{turn.customerMessage}</div>
          <div className="bot-response">{turn.botResponse}</div>
          
          {/* Show ads for product recommendations */}
          {turn.intent === 'product_inquiry' && (
            <AdsOverAI
              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
                });
              }}
            />
          )}
        </div>
      ))}
    </div>
  );
}

Multi-Language Support

import { AdsOverAI } from '@adsoverai/react';

function MultiLangChat({ language, query, response }) {
  return (
    <AdsOverAI
      query={query}
      response={response}
      // Ads will be matched based on content language
      onAdLoad={(ads) => {
        console.log(`Loaded ${ads.length} ads for ${language}`);
      }}
    />
  );
}

Custom Analytics Integration

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 (
    <AdsOverAI
      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

Ads Not Showing

Problem: No ads are displayed, only skeleton or nothing.

Solutions:

  1. Verify API Key

    // Enable debug mode to see console logs
    <AdsOverAIProvider apiKey="your-key" debugMode={true}>
  2. Check Network Requests

    • Open DevTools → Network tab
    • Look for requests to your API endpoint
    • Check response status and body
  3. Verify Query Content

    // Query should have meaningful content
    <AdsOverAI
      query="laptop"  // ❌ Too short
      query="I need a laptop for video editing"  // ✅ Good
    />
  4. Check for Errors

    const { ads, error } = useAdsOverAI({ ... });
    if (error) console.error('Ad fetch error:', error);

Impressions Not Tracking

Problem: onAdImpression callback never fires.

Solutions:

  1. Using Custom Renderer? Wrap with ImpressionWrapper!

    // ❌ Wrong - no impression tracking
    customAdRenderer={(ads) => (
      <div>{ads.map(ad => <div>{ad.product_name}</div>)}</div>
    )}
       
    // ✅ Correct - with ImpressionWrapper
    customAdRenderer={({ ads, ImpressionWrapper }) => (
      <div>
        {ads.map(ad => (
          <ImpressionWrapper key={ad.ad_id} ad={ad}>
            <div>{ad.product_name}</div>
          </ImpressionWrapper>
        ))}
      </div>
    )}
  2. Verify Impression Tracking is Enabled

    <AdsOverAIProvider
      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

    <AdsOverAIProvider
      apiKey="your-api-key"  // ← Required
      apiUrl="https://www.adsoverai.com"  // ← Required
      impressionTracking={{
        enabled: true,
        viewportThreshold: 0.5,
        visibilityDuration: 1000
      }}
    />

TypeScript Errors

Problem: Type errors when using the SDK.

Solutions:

  1. Install Type Definitions

    npm install --save-dev @types/react @types/react-dom
  2. Verify TypeScript Configuration

    // tsconfig.json
    {
      "compilerOptions": {
        "jsx": "react-jsx",
        "esModuleInterop": true,
        "skipLibCheck": true
      }
    }
  3. Import Types Explicitly

    import type { Ad, ImpressionEvent } from '@adsoverai/react';

Styling Conflicts

Problem: SDK styles conflict with your app's CSS.

Solutions:

  1. Import SDK Styles Last

    import './app.css';
    import '@adsoverai/react/styles'; // ← Import after your CSS
  2. Use CSS Specificity

    /* Your overrides */
    .my-app .adsoverai-message {
      background: custom-color;
    }
  3. CSS Variables

    .adsoverai-message {
      --adsoverai-bg: your-color;
    }

Next.js Build Errors

Problem: Errors during Next.js build.

Solutions:

  1. Mark Component as Client-Side

    'use client';  // ← Add this at top of file
    import { AdsOverAI } from '@adsoverai/react';
  2. Dynamic Import (Pages Router)

    import dynamic from 'next/dynamic';
       
    const AdsOverAI = dynamic(
      () => import('@adsoverai/react').then(mod => mod.AdsOverAI),
      { ssr: false }
    );

Performance Issues

Problem: Ads loading slowly or causing lag.

Solutions:

  1. Leverage SWR Caching

    // Ads are cached for 5 minutes by default
    // Subsequent renders with same query are instant
  2. Limit Max Ads

    <AdsOverAI maxAds={2} />  // Show fewer ads
  3. Use Skeleton Variants Wisely

    // Minimal skeleton for faster perceived load
    <AdsOverAIProvider skeletonVariant="minimal">
  4. Optimize Custom Renderers

    // Avoid heavy computations in custom renderer
    customAdRenderer={React.memo(({ ads, ImpressionWrapper }) => (
      // Memoized rendering logic
    ))}

Performance

Bundle Size

  • Core SDK: ~15KB gzipped
  • Styles: ~3KB gzipped
  • Total: ~18KB gzipped

The SDK is tree-shakable - import only what you need:

// Import specific components
import { AdsOverAI } from '@adsoverai/react';  // Smaller bundle

// vs importing everything
import * as AdsOverAI from '@adsoverai/react';  // Larger bundle

Caching Strategy

SWR-Powered Intelligent Caching:

// First render - fetches from API
<AdsOverAI query="laptops" response="..." />

// Second render with same query - instant from cache
<AdsOverAI query="laptops" response="..." />

// 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

Rendering Optimizations

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

Supported Browsers

  • ✅ 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

Required APIs

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)

Polyfills

If you need to support older browsers:

npm install intersection-observer
// In your app entry point
import 'intersection-observer';
import { AdsOverAIProvider } from '@adsoverai/react';

Contributing

We welcome contributions! Here's how you can help:

Development Setup

# Clone the repository
git clone https://github.com/adsoverai/react-sdk.git
cd react-sdk

# Install dependencies
npm install

# Run development build
npm run dev

# Run tests
npm test

# Run tests with UI
npm run test:ui

# Type checking
npm run typecheck

# Linting
npm run lint
npm run lint:fix

# Format code
npm run format

Project Structure

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

Guidelines

  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

Pull Request Process

  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

Additional Resources

Related Packages

  • @adsoverai/vue - Vue.js SDK (coming soon)
  • @adsoverai/angular - Angular SDK (coming soon)
  • @adsoverai/vanilla - Vanilla JavaScript SDK (coming soon)

Support

Need help? We're here for you:

Get in Touch

Commercial Support

For enterprise support, custom integrations, or priority bug fixes:


License

MIT © AdsOverAI Team

See LICENSE for more information.


Acknowledgments

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