Build Status TypeScript React License
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/react
yarn add @adview/react
AdView 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 test
To 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.html
npm 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 files
For 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 major
The 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%2freact
Solution: Ensure you have publishing permissions for the @adview scope.
npm ERR! code E401 npm ERR! 401 Unauthorized
Solution: Run npm login again.
npm ERR! code E409 npm ERR! Cannot publish over existing version
Solution: 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 publish
The @adview/react-popunder package provides a React wrapper component for the PopUnder script, allowing seamless integration into React applications.
npm install @adview/react-popunder
import { 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