AdView is a modern, type-safe React library for displaying and managing advertisements in web applications. It provides flexible components for multiple ad formats with built-in tracking, error handling, and both client-side and server-side rendering support.
- 🎯 Multiple Ad Formats: Support for banner, native, and proxy advertisements
- 🔄 Dual Rendering: Both client-side and server-side rendering capabilities
- 📊 Built-in Tracking: Automatic impression and click tracking
- 🛡️ Type Safety: Full TypeScript support with comprehensive type definitions
- 🎨 Customizable Styling: Flexible styling system for different ad formats
- 🚀 Performance Optimized: Lazy loading and efficient bundle splitting
- đź”§ Extensible: Plugin-based scraper system for data collection
- ⚡ Next.js App Router: Full compatibility with Next.js 13+ App Router
This monorepo contains the following packages:
@adview/core: Core utilities, types, and shared functionality@adview/react: React components and hooks for AdView@adview/native: Vanilla JavaScript SDK for advertisement integration and rendering@adview/popunder: Lightweight JavaScript library for popunder advertisements@adview/react-popunder: React wrapper component for the PopUnder script
- Installation
- Quick Start
- API Reference
- Ad Formats
- Configuration
- Tracking
- Server-Side Rendering
- Next.js App Router
- PopUnder Package
- Development
- Contributing
- License
Install the package using npm or yarn:
npm install @adview/reactyarn add @adview/reactAdView supports multiple import styles for different use cases:
// React components - namespace import
import * as AdView from '@adview/react';
Build all packages
// React components - named imports
import { Provider, Unit, Template, DefaultTemplate } from '@adview/react';
// Server components (for Next.js App Router)
import { Unit as ServerUnit } from '@adview/react/server';// Core utilities (tree-shakable)
import { adViewFetcher, getResolveConfig } from '@adview/core/utils';
// TypeScript types
import { AdViewData, AdViewConfig } from '@adview/core/typings';Benefits:
- Tree-shaking: Import only what you need
- Type safety: Full TypeScript support
- Intellisense: Better IDE autocomplete
- Future-proof: Ready for framework extensions (@adview/vue, @adview/angular)
import * as AdView from '@adview/react';
function MyComponent() {
return (
<AdView.Unit
unitId="your-ad-unit-id"
srcURL="https://your-ad-server.com/ads/{<id>}"
format="banner"
/>
);
}import * as AdView from '@adview/react';
function App() {
return (
<AdView.Provider srcURL="https://your-ad-server.com/ads/{<id>}">
<AdView.Unit unitId="header-banner" format="banner" />
<AdView.Unit unitId="sidebar-native" format="native" />
</AdView.Provider>
);
}import * as AdView from '@adview/react';
function CustomAd() {
return (
<AdView.Unit unitId="custom-ad" format="native">
{({ data, state, error, onDefault }) => {
if (state.isLoading) return <div>Loading ad...</div>;
if (state.isError) return <div>Failed to load ad</div>;
if (!data) return onDefault?.() || null;
return (
<div className="custom-ad">
<h3>{data.fields?.title}</h3>
<p>{data.fields?.description}</p>
</div>
);
}}
</AdView.Unit>
);
}import * as AdView from '@adview/react';
function AdvancedAd() {
return (
<AdView.Unit unitId="multi-format-ad" format={['native', 'banner', 'proxy']}>
{/* Loading state for all formats */}
<AdView.Template type="*" isLoading={true}>
<div className="loading-placeholder">
Loading ad...
</div>
</AdView.Template>
{/* Banner ad template */}
<AdView.Template type="banner">
{({ data }) => {
const mainAsset = data?.assets?.find(asset => asset.name === 'main');
return (
<div className="banner-ad">
<a href={data?.url} target="_blank" rel="noopener">
<img
src={mainAsset?.path}
alt={data?.fields?.title}
style={{ width: '100%', height: 'auto' }}
/>
</a>
</div>
);
}}
</AdView.Template>
{/* Native ad template */}
<AdView.Template type="native">
{({ data }) => {
const mainAsset = data?.assets?.find(asset => asset.name === 'main');
return (
<div className="native-ad">
<a href={data?.fields?.url} target="_blank" rel="noopener">
<img src={mainAsset?.path} alt={data?.fields?.title} />
<h3>{data?.fields?.title}</h3>
<p>{data?.fields?.description}</p>
<span>{data?.fields?.brandname}</span>
</a>
</div>
);
}}
</AdView.Template>
{/* Proxy template for iframe-based ads */}
<AdView.ProxyTemplate className="proxy-ad-container" />
{/* Fallback template */}
<AdView.DefaultTemplate>
<div className="fallback-ad">
<iframe
src="https://your-fallback-ad.com"
width="100%"
height="240"
frameBorder="0"
/>
</div>
</AdView.DefaultTemplate>
</AdView.Unit>
);
}Main component for displaying advertisements.
| Prop | Type | Required | Description |
|---|---|---|---|
unitId |
string |
âś“ | Unique identifier for the ad unit |
format |
'banner' | 'native' | 'proxy' | string[] |
âś— | Ad format type or array of formats |
srcURL |
string |
âś— | Ad server URL template |
onDefault |
() => ReactNode | ReactNode |
âś— | Fallback content when no ad is available |
children |
function | ReactElement |
âś— | Custom render function or component |
The component provides detailed loading states:
type AdLoadState = {
isInitial: boolean; // Initial state before loading
isLoading: boolean; // Currently fetching ad data
isError: boolean; // Error occurred during loading
isComplete: boolean; // Loading completed (success or error)
};Context provider for global configuration.
| Prop | Type | Description |
|---|---|---|
srcURL |
string |
Default ad server URL template |
children |
ReactNode |
Child components |
Template component for custom ad rendering with specific ad types.
| Prop | Type | Description |
|---|---|---|
type |
'banner' | 'native' | 'proxy' | '*' |
Ad type for template matching. Use '*' for all types |
isLoading |
boolean |
Only render when in loading state |
isError |
boolean |
Only render when in error state |
isComplete |
boolean |
Only render when loading is complete |
children |
function |
Render function receiving ad data and state |
Pre-built template for proxy (iframe-based) ads.
| Prop | Type | Description |
|---|---|---|
className |
string |
CSS class for the iframe container |
style |
React.CSSProperties |
Inline styles for the iframe |
<AdView.Unit unitId="proxy-ad">
<AdView.ProxyTemplate className="ad-iframe" />
</AdView.Unit>Default fallback template when no ad data is available.
<AdView.Unit unitId="example">
<AdView.DefaultTemplate>
<div>No ad available</div>
</AdView.DefaultTemplate>
</AdView.Unit>Simple image-based advertisements:
<AdView.Unit unitId="banner-300x250" format="banner" />Content-style ads with structured data:
<AdView.Unit unitId="native-article" format="native" />Delegated rendering to external systems:
<AdView.Unit unitId="proxy-widget" format="proxy" /># Default ad server URL
ADSERVER_AD_JSONP_REQUEST_URL=https://your-ad-server.com/ads/{<id>}The srcURL should contain {<id>} placeholder that will be replaced with the unitId:
https://ads.example.com/serve/{<id>}?format=json
The library automatically collects browser data for ad targeting:
- Screen dimensions
- Timestamp
- Cache-busting tokens
You can extend data collection by adding custom scrapers:
import { pageScrapers } from '@adview/core/utils';
// Add custom scraper
pageScrapers.push(() => ({
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
}));The library automatically handles:
- Impression tracking: When ads become visible
- Click tracking: When users interact with ads
- View tracking: Custom view events
import { AdViewUnitTracking } from '@adview/react';
<AdViewUnitTracking
impressions={['https://track.example.com/imp?id=123']}
clicks={['https://track.example.com/click?id=123']}
views={['https://track.example.com/view?id=123']}
>
<YourAdComponent />
</AdViewUnitTracking>For SSR applications, use the server-specific components:
import { Unit as ServerUnit } from '@adview/react/server';
// In your server component
function ServerPage() {
return (
<ServerUnit
unitId="ssr-banner"
srcURL="https://ads.example.com/serve/{<id>}"
/>
);
}For interactive ads with loading states and user interactions, mark your component as a client component:
'use client';
import * as AdView from '@adview/react';
export default function ClientAdComponent() {
return (
<AdView.Provider srcURL="https://ads.example.com/serve/{<id>}">
<AdView.Unit unitId="interactive-ad" format="native">
{({ data, state, error }) => {
if (state.isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
if (!data) return null;
return (
<div className="ad-content">
<h3>{data.fields?.title}</h3>
<p>{data.fields?.description}</p>
</div>
);
}}
</AdView.Unit>
</AdView.Provider>
);
}For static ads that don't require client-side interactivity:
import { Unit as ServerUnit } from '@adview/react/server';
export default function ServerAdComponent() {
return (
<ServerUnit
unitId="static-banner"
srcURL="https://ads.example.com/serve/{<id>}"
format="banner"
/>
);
}This occurs when using render functions in Server Components. Solution:
- Add
'use client'directive to your component - Or use server-specific components from
@adview/react/server
The @adview/popunder package provides a standalone JavaScript library for creating popunder advertisements. It's designed to be lightweight and work independently of the React components.
- âś… Cross-browser compatibility (Chrome, Firefox, Safari, Edge, Opera)
- âś… Mobile device support (iOS, Android, Windows Phone)
- âś… AdBlock detection
- âś… Cookie-based and click-based frequency control
- âś… Flexible CSS selector targeting
- âś… Customizable parameters and templates
- âś… Built-in analytics and tracking
<script
type="text/javascript"
src="./dist/popunder.js"
data-ad-template="https://ads.example.com/{unitid}/redirect"
data-ad-unitid="your_unit_id"
data-ad-target="a"
data-ad-every="1h30m"
data-ad-every-direct="3"
async
defer
></script>For detailed PopUnder documentation, see packages/popunder/README.md.
- Node.js ≥ 18
- npm or yarn
# Clone the repository
git clone https://github.com/geniusrabbit/adview.git
cd adview
# Install dependencies
npm install
# Build all packages
npm run build
# Start development mode
npm run dev# Development mode for popunder
npm run popunder:dev
# Build popunder package
npm run popunder:build
# Clean popunder build files
npm run popunder:clean# Development mode for react-popunder
npm run react-popunder:dev
# Build react-popunder package
npm run react-popunder:build
# Clean react-popunder build files
npm run react-popunder:clean# Build all packages
npm run build
# Run linting
npm run lint
# Run tests
npm run testTo test the PopUnder functionality:
# Navigate to popunder package
cd packages/popunder
# Serve test files locally
npx http-server . -p 8080
# Open browser and navigate to:
# http://localhost:8080/test.htmlnpm run build # Build all packages
npm run lint # Run ESLint
npm run test # Run tests
npm run version # Version packages
npm run release # Publish packages
npm run popunder:dev # Development mode for popunder
npm run popunder:build # Build popunder package
npm run popunder:clean # Clean popunder build filesFor detailed instructions on publishing packages to npm, see PUBLISHING.md.
# Check packages are ready for publishing
npm run build
npm run lint
npm run test
# Login to npm (first time only)
npm login
# Publish all packages
npm run publish
# Or publish specific package
cd packages/popunder
npm publish
cd packages/react
npm publish# Check current versions
npm run version-check
# Bump versions
npm version patch # or minor, major
npm version minor
npm version majorThe repository includes GitHub Actions for automated publishing:
-
Push a version tag to trigger publishing:
git tag v1.0.1 git push origin v1.0.1
-
GitHub Actions automatically:
- Builds all packages
- Runs tests and linting
- Publishes to npm registry
- Creates GitHub release
Three GitHub Actions workflows are configured:
ci.yml: Runs tests, lint, build on PRs and pushespublish.yml: Publishes packages when version tags are pushedrelease.yml: Manages changeset-based releases on main branch
To add support for new frameworks (Vue, Angular, etc.):
# Create new package structure
mkdir -p packages/vue
cd packages/vue
# Initialize package
npm init -y
# Install dependencies and implement
# ... implement Vue components ...
# Build and test
npm run build
npm publish --dry-run- PopUnder not showing: Check browser popup blockers, verify required attributes
- Frequency problems: Clear cookies/localStorage, check time format
- Mobile issues: Test on actual devices, verify touch events
npm ERR! code E403
npm ERR! 403 Forbidden - PUT https://registry.npmjs.org/@adview%2freactSolution: Ensure you have publishing permissions for the @adview scope.
npm ERR! code E401
npm ERR! 401 UnauthorizedSolution: Run npm login again.
npm ERR! code E409
npm ERR! Cannot publish over existing versionSolution: Increment version number in package.json.
For automated publishing in CI/CD pipelines:
# Use npm token for authentication
echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc
# Build and publish
npm run build
npm run publishThe @adview/react-popunder package provides a React wrapper component for the PopUnder script, allowing seamless integration into React applications.
npm install @adview/react-popunderimport { AdPopunder } from '@adview/react-popunder';
function MyComponent() {
return (
<AdPopunder
unitId="your_unit_id"
template="https://ads.example.com/{unitid}/redirect"
target="a"
every="1h30m"
every-direct={3}
/>
);
}The React PopUnder package uses tsup for building both ES modules and CommonJS versions:
- ESM:
dist/index.mjs- for modern bundlers - CJS:
dist/index.cjs- for Node.js and older bundlers - Types:
dist/index.d.ts- TypeScript declarations
# Development mode for react-popunder
npm run react-popunder:dev
# Build react-popunder package
npm run react-popunder:build
# Clean react-popunder build files
npm run react-popunder:clean