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

react-google-ads-advanced

v2.0.0

Published

A React component for integrating Google Ads with capability to suppress ads which are unfilled or not loaded, enhancing user experience by avoiding empty ad spaces.

Readme


Upgrading from 1.x

2.0.0 adds reserved space, lazy loading, fallbacks and a script loader. Props are backwards compatible, but three defaults changed.

| Change | Impact | What to do | | --- | --- | --- | | Slots reserve minHeight: 250 by default | Space is held before the ad resolves, then released. This is deliberate — unreserved slots are a leading cause of Cumulative Layout Shift | Match it to your real slot, e.g. minHeight={90}, or minHeight={0} for the old behaviour | | The stylesheet no longer sets z-index: 100 !important | Ads no longer stack above your navigation or dialogs | Set a z-index yourself if a layout genuinely needs one | | The stylesheet no longer sets display: flex | AdSense controls its own layout again | Usually nothing |

<GoogleAdsObserver /> is now only needed for slots created outside React — <GoogleAd /> observes itself. Leaving it mounted is harmless.

The slot is also exposed as a complementary landmark labelled "Advertisement", so screen-reader users can identify and skip it.

New, optional

<AdSenseScript clientId="ca-pub-XXXX" enabled={hasAdConsent} />
<GoogleAd clientId="ca-pub-XXXX" slot="123" lazy={400} fallback={<Newsletter />} />

Why

A plain <ins class="adsbygoogle"> has three problems: it leaves a blank rectangle whenever AdSense has nothing to serve, it reserves no space so the page jumps when the ad arrives, and it requests every slot immediately even the ones far below the fold.

This package fixes all three, and stays out of the way otherwise.

Installation

npm install react-google-ads-advanced
yarn add react-google-ads-advanced
pnpm add react-google-ads-advanced
bun add react-google-ads-advanced

Peer dependencies: react >= 17, react-dom >= 17.

Quick start

import { AdSenseScript, GoogleAd } from "react-google-ads-advanced";
import "react-google-ads-advanced/style.css";

export default function Layout({ children }) {
  return (
    <>
      <AdSenseScript clientId="ca-pub-XXXXXXXXXXXXXXXX" />
      {children}
      <GoogleAd clientId="ca-pub-XXXXXXXXXXXXXXXX" slot="1234567890" />
    </>
  );
}

Next.js App Router: the package ships the "use client" directive, so it imports straight into a server component.

<AdSenseScript />

Loads the AdSense script once per page. Optional — skip it if you already load the script yourself.

| Prop | Type | Default | Description | | --- | --- | --- | --- | | clientId | string | — | Required. Publisher id. | | enabled | boolean | true | Set false to hold off until consent is granted. |

// Only load the script once the visitor has accepted advertising cookies.
<AdSenseScript clientId="ca-pub-XXXX" enabled={consent.ad_storage} />

<GoogleAd />

Core

| Prop | Type | Default | Description | | --- | --- | --- | --- | | clientId | string | — | Required. Publisher id (ca-pub-…). | | slot | string | — | Required. Ad slot id. | | adFormat | string | "auto" | Maps to data-ad-format. | | adFullWidthResponsive | string | "true" | Maps to data-full-width-responsive. | | adLayout | string | — | For in-article / in-feed units. | | adLayoutKey | string | — | Maps to data-ad-layout-key. | | className / style | — | — | Applied to the slot. |

Any other prop is spread onto the underlying <ins>.

Layout stability

| Prop | Type | Default | Description | | --- | --- | --- | --- | | minHeight | number \| string | 250 | Space reserved until the outcome is known, then released. |

Unreserved ad slots are one of the most common causes of Cumulative Layout Shift, so this defaults to 250 rather than 0. Match it to the slot you configured in AdSense:

<GoogleAd clientId="ca-pub-XXXX" slot="123" minHeight={90} />

Lazy loading

| Prop | Type | Default | Description | | --- | --- | --- | --- | | lazy | boolean \| number | false | Defer the request until the slot nears the viewport. true uses a 200px margin; a number sets your own, in px. |

<GoogleAd clientId="ca-pub-XXXX" slot="123" lazy={400} />

Off by default: a slot that is never requested never earns, so lazy-load the ones well below the fold rather than all of them.

Unfilled slots

| Prop | Type | Default | Description | | --- | --- | --- | --- | | collapseOnUnfilled | boolean | true | Hide the slot when AdSense has no ad. | | fallback | ReactNode | — | Render this instead when unfilled. Implies collapsing. | | onFilled / onUnfilled | () => void | — | Outcome callbacks. |

<GoogleAd
  clientId="ca-pub-XXXX"
  slot="123"
  fallback={<NewsletterSignup />}
  onUnfilled={() => analytics.track("ad_unfilled")}
/>

Other

| Prop | Type | Default | Description | | --- | --- | --- | --- | | label | string | "Advertisement" | Accessible label. The slot is exposed as a complementary landmark so screen-reader users can identify and skip it. | | refreshKey | string \| number | — | Change it to request a fresh ad into the same slot. |

How unfilled detection works

data-ad-status is AdSense's own verdict, so it is trusted whenever present. Only when AdSense sets no status at all does the component fall back to inspecting the slot's contents.

That distinction matters: a filled slot renders a cross-origin iframe whose children are not visible from your document, so treating "looks empty" as unfilled would collapse perfectly good ads.

<GoogleAdsObserver />

Only needed for slots created outside React — GoogleAd manages itself.

<GoogleAdsObserver />              // watches document.body
<GoogleAdsObserver root={myEl} />  // or a subtree

Consent

Personalised advertising needs consent in the EU/UK. Gate the script, and optionally the slots:

<AdSenseScript clientId="ca-pub-XXXX" enabled={hasAdConsent} />

Pair with react-consent-management-banner, which wires Google Consent Mode v2 for you.

Notes

  • Ads do not render on localhost or on an unapproved domain. Empty slots in development are AdSense, not this package.
  • Ad blockers stop the AdSense script loading at all. Pair with react-adblocker-detect to detect that case.
  • The stylesheet sets no z-index — an advert should not stack above your navigation or dialogs. Set one yourself if a layout needs it.

Styling

import "react-google-ads-advanced/style.css";

The stylesheet is deliberately tiny: display: block, full width, and overflow: hidden so a slightly oversized creative cannot introduce a horizontal scrollbar on narrow screens. Everything else is AdSense's own layout, and fighting it with !important causes more problems than it solves.

Contributing

Issues and pull requests are welcome.

git clone https://github.com/faraasat/react-google-ads-advanced.git
cd react-google-ads-advanced
npm install
npm test          # vitest unit tests
npm run typecheck # tsc --noEmit
npm run build     # tsup

End-to-end tests run against the built demo in a real browser (desktop and mobile viewports), and cover the things unit tests cannot: layout, CSS and keyboard behaviour.

npm run build && npm --prefix example install && npm --prefix example run build
npm run test:e2e      # playwright
npm run test:e2e:ui   # interactive

To run the demo site against your local build:

npm run example:dev

Releases are manual — nothing publishes on a push to main. Maintainers run the Release workflow from the Actions tab.

Privacy

The published package contains no telemetry. The demo site at faraasat.github.io/react-google-ads-advanced uses Google Analytics and Aptabase; the library itself never phones home.

License

MIT © Farasat Ali