@weforads/react
v1.0.0
Published
React components for WeForAds ad integration — drop-in ad slots for React/Next.js/Remix SPAs
Maintainers
Readme
@weforads/react
React SDK for WeForAds ad monetization. Drop-in components for React, Next.js, Vite, Remix, and any React SPA.
Handles Google Ad Manager (GPT), Prebid header bidding, lazy loading, ad refresh, viewability tracking, video ads, interstitials, and scroll ads — all automatically.
Table of Contents
- Installation
- Quick Start (3 minutes)
- Integration Methods
- Framework Guides
- Ad Types & Sizes
- Auto Ads (Zero-Code Ads)
- Manual Placement
- Combining Auto Ads + Manual Slots
- SPA Navigation Handling
- API Reference
- Advanced Usage
- Troubleshooting
- FAQ
Installation
Download the SDK files and copy them into your project:
1. Download the src/ folder from WeForAds dashboard (or your account manager)
2. Copy into your project (e.g. src/lib/weforads/):
src/lib/weforads/
WfaProvider.jsx — Context provider, loads tag script
WfaAd.jsx — Ad slot component
useWeForAds.js — Standalone hook (no provider needed)
index.js — Exports3. Import in your code:
import { WfaProvider, WfaAd } from './lib/weforads';Requirements
- React 16.8+ (hooks support)
- Your WeForAds tag token (get it from your WeForAds dashboard → Sites → Tag Token)
Quick Start
Total time: 3 minutes. Two files to edit.
Step 1 — Wrap your app (once)
// src/App.jsx (or your root component)
import { WfaProvider } from './lib/weforads';
function App() {
return (
<WfaProvider token="wfa_YOUR_TAG_TOKEN">
{/* Your existing app */}
<Router>
<Routes />
</Router>
</WfaProvider>
);
}
export default App;Step 2 — Place ads on any page
// src/pages/ArticlePage.jsx
import { WfaAd } from './lib/weforads';
function ArticlePage() {
return (
<div>
{/* Leaderboard ad at top of page */}
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" />
<h1>Article Title</h1>
<p>First paragraph of content...</p>
{/* Rectangle ad in content */}
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />
<p>More content...</p>
<p>Even more content...</p>
{/* Another ad at bottom */}
<WfaAd slot="wfa_8_footer_banner" size="728x90" />
</div>
);
}That's it. WeForAds handles GPT, Prebid, lazy loading, ad refresh, and viewability automatically. No other configuration needed.
Integration Methods
Method 1: Provider + Components
Best for: Full control over ad placement. You decide exactly where each ad appears.
// 1. Wrap app once
<WfaProvider token="wfa_YOUR_TOKEN">
<App />
</WfaProvider>
// 2. Place ads anywhere in your JSX
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" />How it works:
WfaProviderloads the WFA tag script into<head>WfaAdrenders a<div data-wfa-ad="...">into the DOM- WFA's MutationObserver detects the div and initializes the GPT/Prebid slot
- On SPA navigation, WFA auto-detects route changes and re-initializes
Method 2: Hook Only
Best for: Auto Ads only. WeForAds admin decides where ads appear — no code changes needed per page.
import { useWeForAds } from './lib/weforads';
function App() {
useWeForAds('wfa_YOUR_TOKEN');
return <div>Your app — ads are auto-injected by admin rules</div>;
}How it works:
- Hook loads the WFA tag script once
- Auto Ads rules (configured in WeForAds admin) scan the page DOM
- Ads are injected at matching CSS selectors (e.g. after paragraphs, between sections)
- Publisher writes zero ad code — all placement controlled from admin panel
Method 3: Copy-Paste
Best for: Quick test without npm install. Paste directly in your component.
// Just paste this in any component file:
import { useEffect } from 'react';
function useWeForAds() {
useEffect(() => {
if (window.__wfa_loaded) {
if (window.wfa && window.wfa.refresh) window.wfa.refresh();
return;
}
window.__wfa_loaded = true;
const s = document.createElement('script');
s.src = 'https://weforads.com/tag/wfa_YOUR_TOKEN.js';
s.async = true;
document.head.appendChild(s);
}, []);
}
// Use in any page:
function MyPage() {
useWeForAds();
return <div>...</div>;
}Framework Guides
Create React App
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { WfaProvider } from './lib/weforads';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<WfaProvider token="wfa_YOUR_TOKEN">
<BrowserRouter>
<App />
</BrowserRouter>
</WfaProvider>
);Vite + React
// src/main.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { WfaProvider } from './lib/weforads';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<WfaProvider token="wfa_YOUR_TOKEN">
<BrowserRouter>
<App />
</BrowserRouter>
</WfaProvider>
);Next.js (App Router)
// app/layout.jsx
import { WfaProvider } from './lib/weforads';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<WfaProvider token="wfa_YOUR_TOKEN">
{children}
</WfaProvider>
</body>
</html>
);
}// app/article/[slug]/page.jsx
import { WfaAd } from './lib/weforads';
export default function ArticlePage({ params }) {
return (
<article>
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" eager />
<h1>Article Title</h1>
<p>Content...</p>
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />
<p>More content...</p>
</article>
);
}Note for Next.js SSR: The WFA script only loads client-side.
WfaProviderandWfaAdare safe to use in server components — they render empty divs on the server, which hydrate client-side.
Next.js (Pages Router)
// pages/_app.jsx
import { WfaProvider } from './lib/weforads';
export default function MyApp({ Component, pageProps }) {
return (
<WfaProvider token="wfa_YOUR_TOKEN">
<Component {...pageProps} />
</WfaProvider>
);
}Remix
// app/root.jsx
import { WfaProvider } from './lib/weforads';
export default function App() {
return (
<html>
<head><Meta /><Links /></head>
<body>
<WfaProvider token="wfa_YOUR_TOKEN">
<Outlet />
</WfaProvider>
<Scripts />
</body>
</html>
);
}Gatsby
// gatsby-browser.js
import React from 'react';
import { WfaProvider } from './lib/weforads';
export const wrapRootElement = ({ element }) => (
<WfaProvider token="wfa_YOUR_TOKEN">
{element}
</WfaProvider>
);React Native Web
WeForAds works with React Native Web (Expo for web). Use the same components:
import { WfaProvider, WfaAd } from './lib/weforads';
export default function App() {
return (
<WfaProvider token="wfa_YOUR_TOKEN">
<View>
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />
</View>
</WfaProvider>
);
}Note: Only works on web builds. React Native mobile (iOS/Android) does not support GPT ads.
Ad Types & Sizes
Standard Banner
The most common ad format. Place between content sections.
{/* Medium Rectangle — best for in-content */}
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />
{/* Leaderboard — best for top/bottom of page */}
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" />
{/* Large Banner — mobile-friendly */}
<WfaAd slot="wfa_8_mobile_banner" size="320x100" />
{/* Billboard — high impact */}
<WfaAd slot="wfa_8_billboard" size="970x250" />Responsive / Multi-Size
Pass comma-separated sizes. GPT picks the best fit for the viewport.
{/* GPT chooses between 728x90 (desktop) and 320x50 (mobile) */}
<WfaAd slot="wfa_8_responsive_banner" size="728x90,320x50,320x100" />
{/* Multiple rectangles */}
<WfaAd slot="wfa_8_flex_mpu" size="300x250,336x280,300x600" />Sticky Footer
Handled automatically by Auto Ads — no component needed. Configured in WeForAds admin panel under Auto Ads. The sticky footer appears fixed at the bottom of the screen with a close button.
Sticky Header
Also handled by Auto Ads. Configured in admin. Sticks to the top of the page on scroll.
Sidebar Sticky
For sites with a sidebar layout:
<div style={{ display: 'flex' }}>
<main style={{ flex: 1 }}>
<h1>Content</h1>
<p>...</p>
</main>
<aside style={{ width: 300 }}>
<div style={{ position: 'sticky', top: 20 }}>
<WfaAd slot="wfa_8_sidebar_sticky" size="300x250" />
</div>
</aside>
</div>Video Outstream
Video ads are configured in WeForAds admin (Video Ads section). They appear as a sticky bottom-right player automatically — no component needed.
If you want video in-content, enable it in admin with "In-Content" placement.
Auto Ads
Zero code required from the publisher. All ad placements are configured in the WeForAds admin panel.
How Auto Ads Work
- WeForAds admin configures rules: "insert 300x250 after every 3rd paragraph"
- The WFA tag script scans the page DOM for matching elements
- Ads are injected automatically at the configured positions
- Works on React SPAs — MutationObserver detects when React renders content
Setup
Just load the script — either with WfaProvider or useWeForAds:
// Option A: Provider
<WfaProvider token="wfa_YOUR_TOKEN">
<App />
</WfaProvider>
// Option B: Hook
function App() {
useWeForAds('wfa_YOUR_TOKEN');
return <div>...</div>;
}Auto Ads will inject ads based on admin rules. Publisher writes zero ad-related JSX.
What Auto Ads Can Do
All configured from admin — no code changes:
- Insert ads after every Nth paragraph
- Insert ads between sections/headings
- Sticky footer/header banners
- Sidebar rail ads
- Video outstream (sticky player)
- Interstitial ads
- Scroll-triggered ads
- Per-device targeting (mobile/desktop/tablet)
- Geo-targeting (by country)
- URL pattern matching (show on specific pages)
- Frequency capping
Manual Placement
Use <WfaAd /> for precise control over where ads appear.
Basic Placement
import { WfaAd } from './lib/weforads';
function BlogPost({ post }) {
return (
<article>
{/* Top of article */}
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" eager />
<h1>{post.title}</h1>
{post.paragraphs.map((p, i) => (
<React.Fragment key={i}>
<p>{p}</p>
{/* Ad after every 3rd paragraph */}
{(i + 1) % 3 === 0 && (
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />
)}
</React.Fragment>
))}
{/* Bottom of article */}
<WfaAd slot="wfa_8_footer_banner" size="728x90" />
</article>
);
}Eager Loading (Above the Fold)
For ads visible on page load (above the fold), use eager to skip lazy loading:
{/* Loads immediately — don't wait for scroll */}
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" eager />
{/* Below the fold — lazy loads when near viewport */}
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />Custom Styling
<WfaAd
slot="wfa_8_sidebar_mpu"
size="300x250"
className="my-ad-wrapper"
style={{ background: '#f5f5f5', padding: '10px', borderRadius: '8px' }}
/>Combining Auto Ads + Manual Slots
You can use both simultaneously. Auto Ads won't duplicate slots already placed by <WfaAd />.
<WfaProvider token="wfa_YOUR_TOKEN">
<div>
{/* This ad is placed manually by publisher */}
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" eager />
<article>
<h1>Article</h1>
<p>Content...</p>
{/* Auto Ads may inject additional ads here based on admin rules */}
<p>More content...</p>
</article>
</div>
</WfaProvider>SPA Navigation Handling
WeForAds automatically handles SPA navigation by:
- Wrapping
history.pushState/replaceState— detects React Router navigation - Listening to
popstate/hashchange— detects back/forward and hash routing - MutationObserver — detects when React renders new content after navigation
You typically don't need to do anything. But if ads don't refresh on navigation, add a manual refresh:
import { useWfa } from './lib/weforads';
import { useLocation } from 'react-router-dom';
import { useEffect } from 'react';
function AdRefresher() {
const { refresh } = useWfa();
const location = useLocation();
useEffect(() => {
// Small delay lets React finish rendering before WFA scans DOM
const timer = setTimeout(refresh, 200);
return () => clearTimeout(timer);
}, [location.pathname, refresh]);
return null; // Invisible component
}
// Add to your app:
<WfaProvider token="wfa_YOUR_TOKEN">
<BrowserRouter>
<AdRefresher />
<Routes />
</BrowserRouter>
</WfaProvider>API Reference
WfaProvider
Loads the WFA tag script and provides context to child components.
<WfaProvider token="wfa_abc123" cdnUrl="https://weforads.com">
<App />
</WfaProvider>| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| token | string | Yes | — | Your site tag token from WeForAds dashboard |
| cdnUrl | string | No | https://weforads.com | CDN base URL (only change if using custom CDN) |
| children | ReactNode | Yes | — | Your app |
WfaAd
Renders a single ad slot.
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />| Prop | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| slot | string | Yes | — | Ad unit slot name (from WeForAds dashboard) |
| size | string | Yes | — | Ad size(s). Single: "728x90". Multi: "728x90,320x50" |
| eager | boolean | No | false | true = load immediately. false = lazy load on scroll. |
| className | string | No | "" | CSS class name for the wrapper div |
| style | object | No | {} | Inline styles for the wrapper div |
Common sizes:
| Size | Name | Best For |
|------|------|----------|
| 728x90 | Leaderboard | Top/bottom of page |
| 300x250 | Medium Rectangle | In-content, sidebar |
| 320x50 | Mobile Banner | Mobile top/bottom |
| 320x100 | Large Mobile Banner | Mobile in-content |
| 970x250 | Billboard | High-impact desktop |
| 160x600 | Wide Skyscraper | Sidebar |
| 300x600 | Half Page | Sidebar |
| 970x90 | Large Leaderboard | Desktop header |
useWfa()
Hook to access WFA context inside WfaProvider.
const { ready, refresh } = useWfa();| Return | Type | Description |
|--------|------|-------------|
| ready | boolean | true when tag script has loaded |
| refresh | () => void | Manually trigger ad slot re-initialization |
useWeForAds()
Standalone hook — no provider needed. Loads the WFA script and handles deduplication.
useWeForAds('wfa_YOUR_TOKEN');
useWeForAds('wfa_YOUR_TOKEN', 'https://custom-cdn.com'); // optional CDN URL| Param | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| token | string | Yes | — | Your site tag token |
| cdnUrl | string | No | https://weforads.com | CDN base URL |
Advanced Usage
Conditional Ads
Show ads only on certain pages or for certain users:
function ArticlePage({ isPremiumUser, article }) {
return (
<article>
<h1>{article.title}</h1>
{/* No ads for premium users */}
{!isPremiumUser && (
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />
)}
<p>{article.body}</p>
{/* Ads only on long articles */}
{article.wordCount > 500 && !isPremiumUser && (
<WfaAd slot="wfa_8_footer_banner" size="728x90" />
)}
</article>
);
}Manual Refresh
Force re-initialization of all ad slots (e.g. after dynamic content loads):
import { useWfa } from './lib/weforads';
function DynamicContent() {
const { refresh } = useWfa();
const loadMore = async () => {
await fetchMoreContent();
// Content changed — tell WFA to re-scan for new ad positions
refresh();
};
return <button onClick={loadMore}>Load More</button>;
}Ad Events & Callbacks
Listen for ad lifecycle events via the global googletag API:
import { useEffect } from 'react';
import { useWfa } from './lib/weforads';
function AdTracker() {
const { ready } = useWfa();
useEffect(() => {
if (!ready || !window.googletag) return;
googletag.cmd.push(() => {
googletag.pubads().addEventListener('slotRenderEnded', (event) => {
console.log('Ad rendered:', event.slot.getSlotElementId(), {
isEmpty: event.isEmpty,
size: event.size,
});
});
googletag.pubads().addEventListener('impressionViewable', (event) => {
console.log('Ad viewable:', event.slot.getSlotElementId());
});
});
}, [ready]);
return null;
}A/B Testing Ad Placements
function ArticlePage() {
// Simple A/B: show leaderboard vs rectangle at top
const variant = useMemo(() => Math.random() > 0.5 ? 'A' : 'B', []);
return (
<article>
{variant === 'A' ? (
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" />
) : (
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" />
)}
<h1>Article</h1>
</article>
);
}Programmatic Destroy
Clean up all ad slots (e.g. before unmounting a section):
function CleanupExample() {
useEffect(() => {
return () => {
// Destroy all WFA slots on unmount
if (window.wfa && window.wfa.destroy) window.wfa.destroy();
};
}, []);
return <div>...</div>;
}Troubleshooting
Ads not showing
- Check the tag token — ensure it matches your site in WeForAds dashboard
- Open browser DevTools (F12) → Console — look for
[WFA]messages - Check Network tab — verify
wfa_YOUR_TOKEN.jsloads with status 200 - Use Google Publisher Console — add
?googfcto your URL to see GPT debug info - Verify ad units exist — the slot name in
<WfaAd slot="...">must match an ad unit in your WeForAds dashboard
"Ad unit did not fill"
This means the ad slot is working correctly but no advertiser bid on it. Causes:
- New site with low traffic — give it a few days for demand to build
- Check if Prebid bidders are configured in WeForAds admin
- Check floor prices — lower them if they're too high
- Check line items in Google Ad Manager targeting this ad unit
Ads load but are invisible
- Check parent CSS —
overflow: hiddenorheight: 0on a parent can clip ads - Ensure the ad container has visible dimensions
- Try adding
eagerprop to skip lazy loading:<WfaAd slot="..." size="..." eager />
Ads don't refresh on SPA navigation
Add the AdRefresher component (see SPA Navigation Handling).
Console error: querySelector
If you see a querySelector error, you're using an old version of the hook. Update to the latest useWeForAds which uses window.__wfa_loaded flag instead of querySelector.
Multiple script loads
WfaProvider prevents this automatically. If using useWeForAds, it deduplicates via window.__wfa_loaded. Never load the script tag in both index.html AND React — pick one.
FAQ
Q: Do I need to add the script tag in index.html?
No. WfaProvider (or useWeForAds) loads the script automatically. Don't add it to index.html — it will double-load.
Q: Can I use Auto Ads and <WfaAd /> together?
Yes. They work together without conflicts. Auto Ads inject at CSS selectors, <WfaAd /> renders where you place it. No duplication.
Q: Does this work with Server-Side Rendering (SSR)? Yes. Components render empty divs on the server. The WFA script loads client-side on hydration.
Q: Do I need to handle cleanup on route changes? No. WFA automatically detects SPA navigation and cleans up old slots before creating new ones.
Q: Will ads slow down my page? No. All ad scripts load asynchronously. Below-the-fold ads use lazy loading by default. The WFA script is ~30KB gzipped.
Q: How do I get my tag token? Log in to your WeForAds dashboard → Sites → click your site → Tag Token is shown in the integration section.
Q: What if my site uses Material UI / Tailwind / Chakra UI?
Works perfectly. <WfaAd /> renders a standard <div> that you can style with any CSS framework:
<WfaAd slot="wfa_8_sidebar_mpu" size="300x250" className="my-4 mx-auto" />Q: Can I show different ads on mobile vs desktop? Yes, in two ways:
- GPT responsive sizing: Pass multi-size
size="728x90,320x50"— GPT picks the right size - Conditional rendering: Use responsive hooks to render different components:
const isMobile = useMediaQuery('(max-width: 767px)');
{isMobile ? (
<WfaAd slot="wfa_8_mobile_banner" size="320x50" />
) : (
<WfaAd slot="wfa_8_header_leaderboard" size="728x90" />
)}Support
- Dashboard: https://weforads.com
- Email: [email protected]
- Report issues: Contact your WeForAds account manager
License: MIT
