genuin-react-native-ads-v3 puts a Genuin IAB ad slot on screen as a single React component. You supply a tag ID; the package renders the SDK’s real ad experience and reports the full analytics funnel on its own. There is no player to wire up, no creative to fetch, and no measurement code to add.

Android only. GenuinAd renders nothing on iOS and initialize() rejects there, so shared screens stay mountable but no ad appears.

This guide is the React Native counterpart to Genuin’s Integrate the Genuin Ad SDK for Android guide.

What you get

One <GenuinAd /> gives you the SDK’s own ad UI: a shimmer placeholder while the waterfall resolves, mute and play/pause controls, a fullscreen affordance with proper activity handoff, karaoke captions on audio ads, and CTA link-out into an in-app browser.

Ad measurement is automatic. Impressions, quartiles, clicks, viewability and error reporting all flow through the SDK’s own pipeline the moment a slot renders. onAdEvent is a UI/lifecycle stream for your own logic — you do not need to subscribe to it for measurement to work.

The slot sizes itself from the format you pick, so a placement is a two-prop component in the common case, and the six IAB sizes cover standard banner through half-page.

Before you start

You need a brand API key and at least one IAB tag ID from the Genuin dashboard. The tag ID is the only per-slot input.

Step 1 — Install

index.html
npm install genuin-react-native-ads-v3
# or
yarn add genuin-react-native-ads-v3

Autolinking registers the native module. Nothing to add to MainApplication, nothing to add to MainActivity, and no manifest edits.

Step 2 — Android setup

Two things in your app’s Gradle. The package brings the SDK artifacts with it at a pinned version.

Toolchain versions. React Native libraries read these from rootProject.ext, so your app decides what this module compiles with. Setting them is not optional.

index.html
kotlin
// android/build.gradle
buildscript {
    ext {
        minSdkVersion = 24
        compileSdkVersion = 36
        targetSdkVersion = 36
        kotlinVersion = "2.0.21"
    }
}

Kotlin must be 2.0.21 or newer — the SDK is compiled with it and Kotlin 1.9.x cannot read 2.x metadata. compileSdk must be 36. Java/JVM target must be 17. Android Gradle Plugin 8.9.1+.

Core-library desugaring. This is the step most likely to bite you, and the build error it produces (Invoke-customs, or missing java.time) names nothing about Genuin.

index.html
kotlin
// android/app/build.gradle
android {
    compileOptions {
        coreLibraryDesugaringEnabled true
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

dependencies {
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
}

React Native 0.83’s template already enables this with exactly this artifact, so on a current scaffold it is usually already there — check before assuming. A bare app on an older React Native, or a hand-rolled Gradle setup, may not have it.

Permissions. Nothing to add. The SDK merges in what it needs, including INTERNET and ACCESS_NETWORK_STATE. One item does need a decision from you: the ads engine brings com.google.android.gms.permission.AD_ID, and an app shipping it must declare advertising-ID use in the Play Console Data Safety form or the submission is rejected.

Step 3 — Initialize

initialize() must resolve before any slot mounts. Until it does, the native view throws IllegalStateException("SDK not initialized…"), so gate on state rather than firing and forgetting.

index.html
import { Platform } from 'react-native';
import { initialize, isInitialized } from 'genuin-react-native-ads-v3';

if (Platform.OS === 'android') {
  await initialize({ apiKey: 'YOUR_BRAND_API_KEY', env: 'PROD' });
}

You can check initialisation of SDK via isInitialized().

Step 4 — Render a slot

index.html
import { GenuinAd, GenuinAdFormat } from 'genuin-react-native-ads-v3';

<GenuinAd tagId="YOUR_TAG_ID" format={GenuinAdFormat.SIZE_300X250} />;

tagId is the only required prop. You do not size the slot — the component applies the format’s fixed dp size itself, because the native view lays its content out at that size regardless of the box React Native gives it.

Example implementation

index.html
import { useEffect, useState } from 'react';
import { Platform } from 'react-native';
import {
  initialize,
  GenuinAd,
  GenuinAdFormat,
  type GenuinAdEvent,
} from 'genuin-react-native-ads-v3';

export default function ArticleScreen() {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    if (Platform.OS !== 'android') return;
    initialize({ apiKey: 'YOUR_BRAND_API_KEY', env: 'PROD' })
      .then(() => setReady(true))
      .catch(console.error);
  }, []);

  return (
    <>
      <ArticleBody />
      {ready && (
        <GenuinAd
          tagId="YOUR_TAG_ID"
          format={GenuinAdFormat.SIZE_300X250}
          onAdEvent={(event: GenuinAdEvent) => console.log(event)}
        />
      )}
    </>
  );
}

The repo’s example/ app renders one tag at all six sizes with a live event readout under each — run it with yarn example android.

Key configuration parameters

initialize(options)

OptionTypeNotes
apiKeystringRequired. Brand API key from the dashboard.
env'PROD' | 'QA'Required.
fontFamilystringOmit for the SDK default.
audioBehaviorConfig.duckVolumeDuringTransientCanDucknumber0…1, default 0.15. Out-of-range values fall back to the default rather than crashing init.
audioBehaviorConfig.focusLossHandling'MUTE_OUTPUT' | 'PAUSE_PLAYBACK'Default MUTE_OUTPUT.

<GenuinAd />

PropTypeNotes
tagIdstringRequired. IAB tag ID for the placement.
formatGenuinAdFormatDefaults to SIZE_300X250. Exported as a value, so format={GenuinAdFormat.SIZE_320X50} gives you autocomplete; format="SIZE_320X50" also typechecks.
uniqueIdstringRequired only when two slots share a tagId and format on one screen.
onAdEvent(event: GenuinAdEvent) => voidLifecycle stream. Optional — analytics do not depend on it.
styleViewStylePlacement only: margins, alignSelf. Dimensions are applied for you; a width/height here overrides them.

Multiple slots on one screen need distinct uniqueIds, because the SDK keys its per-slot state on iab_<tagId>_<format>_<uniqueId>:

Ad formats

formatSize (dp)Typical use
SIZE_320X50320 × 50Standard banner — headers, footers
SIZE_320X100320 x 100Large banner
SIZE_300X250300 × 250Medium rectangle — in-article, mid-feed
SIZE_320X480320 × 480Half-page portrait — interstitial-style blocks
SIZE_300X600300 × 600Half-page filmstrip
SIZE_300X250_COLLAPSIBLE300 × 250, collapsing to 300 × 50Sticky placements that shrink after first view

Lifecycle events

onAdEvent receives a discriminated union. Ad positions are 1-based.

event.typePayloadMeaning
loadingFeed began loading
loadedcountFeed resolved with renderable ads
noAdsFeed resolved but empty
appearedindex, total, adTypeAd became active on-screen
startedindex, total, adTypePlayback began
completedindex, total, adTypeThis ad played through to the end. Only a real playout reports it — an ad that never filled reports noFill instead. Per-ad; the slot-level counterpart is allAdsCompleted.
changedfrom, toPager moved between ads
loadTimeoutindex, total, adType, timeoutMsThe waterfall budget elapsed before this ad resolved. Informational — the waterfall is not cancelled and still reports started or noFill afterwards.
noFillindex, total, adTypeAd waterfall produced no fill
allAdsCompletedtotalEvery ad in the slot is done and none is pending. Slot-level rather than per-ad — the cue to collapse the slot or resume your own content. Fires once per completed pass, so a slot that resolves a fresh ad reports it again.
collapsedisCollapsedThe collapsible slot was collapsed or expanded; isCollapsed is the resulting state. Never fires for the fixed sizes.

These eleven are the complete set.

Specs and limitations

On web, importing the package throws, because the module resolves a native module at import time.

Initialize before mounting. A slot mounted before initialize() resolves throws natively.

The collapsible format needs you to resize the box. It shrinks to 50 dp natively while the React Native box stays at 250, because React Native does not re-lay-out a native child that resizes itself. Listen for the collapsed event and drive style={{ height }} from your own state — GENUIN_AD_SIZES.SIZE_300X250_COLLAPSIBLE carries both height and collapsedHeight for this.

Android

No pause() / resume(). The SDK drives playback from the Android host lifecycle, and that gate has no public API. With a JS-only router the activity stays resumed, so audio can continue after you navigate away. Unmounting the component does stop it.

Release builds work out of the box. The package applies its own ProGuard/R8 keep rules to your app automatically, verified against a minified release build with R8 full mode on — AGP 8’s default. You should not need to add anything to proguard-rules.pro.

App size. Roughly 11.9 MB added — about 7.2 MB of Genuin code and 4.7 MB of Google IMA.

Example scenarios

News or content app. A SIZE_300X250 slot placed mid-article, mounted once the article loads. One tag ID serves every article; the waterfall decides what fills it.

Scrolling feed. SIZE_320X100 inserted every N rows. Give each instance a distinct uniqueId so the SDK keeps their state apart, and reserve the row height with GENUIN_AD_SIZES so the list does not jump when the slot mounts.

Sticky footer. SIZE_320X50, or SIZE_300X250_COLLAPSIBLE where you want a larger first impression that shrinks afterwards.

Between-content interstitial. SIZE_320X480 or SIZE_300X600 on a transition screen, unmounted as soon as the user moves on — unmounting is what stops playback.

FAQs

No ads in a release build, but debug works. Look for ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType in logcat. That is R8 stripping the generic signatures Retrofit reflects on, and it surfaces as “Ad feed request returned no data” with a single-digit-millisecond latency — an empty ad response, not a client error. This package ships the keep rules that prevent it, so if you hit it, something is excluding them: check that you have not overridden proguardFiles in a way that drops consumer rules.

Every slot shows “no ads available” and logcat is silent. Check that initialize() resolved before the slot mounted, that the apiKey matches the account, that env is right, and that the tagId is non-blank and active in the dashboard.

The ad is clipped, or sits in a block of empty space. A width or height in your style is overriding the format’s own size. Drop the dimensions. The exception is the collapsible format, where the box stays at full height until you resize it from the collapsed event.

The build fails with Invoke-customs or java.time errors. Core-library desugaring is off; see step 2.

Metro logs Codegen didn't run for BGInstreamAdView. Expected and harmless in development.

Kotlin metadata errors when building. Your app’s kotlinVersion is below 2.0.21.

See also

TRUST AND COMPLIANCE

© 2026 Genuin Inc.

Genuin Footer