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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@weforads/react

v1.0.0

Published

React components for WeForAds ad integration — drop-in ad slots for React/Next.js/Remix SPAs

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

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          — Exports

3. 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:

  1. WfaProvider loads the WFA tag script into <head>
  2. WfaAd renders a <div data-wfa-ad="..."> into the DOM
  3. WFA's MutationObserver detects the div and initializes the GPT/Prebid slot
  4. 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:

  1. Hook loads the WFA tag script once
  2. Auto Ads rules (configured in WeForAds admin) scan the page DOM
  3. Ads are injected at matching CSS selectors (e.g. after paragraphs, between sections)
  4. 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. WfaProvider and WfaAd are 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

  1. WeForAds admin configures rules: "insert 300x250 after every 3rd paragraph"
  2. The WFA tag script scans the page DOM for matching elements
  3. Ads are injected automatically at the configured positions
  4. 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:

  1. Wrapping history.pushState / replaceState — detects React Router navigation
  2. Listening to popstate / hashchange — detects back/forward and hash routing
  3. 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

  1. Check the tag token — ensure it matches your site in WeForAds dashboard
  2. Open browser DevTools (F12) → Console — look for [WFA] messages
  3. Check Network tab — verify wfa_YOUR_TOKEN.js loads with status 200
  4. Use Google Publisher Console — add ?googfc to your URL to see GPT debug info
  5. 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: hidden or height: 0 on a parent can clip ads
  • Ensure the ad container has visible dimensions
  • Try adding eager prop 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:

  1. GPT responsive sizing: Pass multi-size size="728x90,320x50" — GPT picks the right size
  2. 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


License: MIT