admob-ads-and-consent
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.
Skill by zakariaf
Chalk Labs
Need help deploying admob-ads-and-consent?
Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/zakariaf-admob-ads-and-consent
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: testing, marketing
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
- Runs as
- Node.js package
Network hosts it talks to 2
codeload.github.com· found in install commanddevelopers.google.com· found in code
Files it touches
~/.claude/skills/admob-ads-and-consent
What's inside
- Skill name
admob-ads-and-consent- Description
- Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs. Use when touching ads, consent, ATT, ad IDs or rewarded perks. Not for purchases (in-app-purchases).
- License
- MIT
- Instructions
- 279 lines in SKILL.md · view on GitHub
Before installing
- Ships executable scripts
Files (128)
- SKILL.md
- admob-facts.json · assets/
- shared.json · assets/
- ad-privacy-row.tsx · examples/
- ads-root.tsx · examples/
- app-config-ads.ts · examples/
- consent-primer.tsx · examples/
- create-device-services.ts · examples/
- tasks-screen.tsx · examples/
- use-settings-model.ts · examples/
- use-tasks-model.ts · examples/
- ad-policy.md · references/
- consent-flow.md · references/
- console-privacy-troubleshooting.md · references/
- placements-and-lifecycle.md · references/
- setup-ids-and-modes.md · references/
- check-ad-behaviour.mjs · scripts/
- check-ads.mjs · scripts/
- check-lib.mjs · scripts/
- ads-facts.mjs · scripts/lib/
- ads-rules.mjs · scripts/lib/
- ads-scenarios.mjs · scripts/lib/
- app-modules.mjs · scripts/lib/
- consent-rules.mjs · scripts/lib/
- consent-scenarios.mjs · scripts/lib/
- load-ts.mjs · scripts/lib/
- repo-scan.mjs · scripts/lib/
- expo-constants.mjs · scripts/lib/stubs/
- expo-tracking-transparency.mjs · scripts/lib/stubs/
- react-native-google-mobile-ads.mjs · scripts/lib/stubs/
- react-native.mjs · scripts/lib/stubs/
- react.mjs · scripts/lib/stubs/
- selftest.mjs · scripts/
- expo-tracking-transparency.ts · templates/__mocks__/
- react-native-google-mobile-ads.ts · templates/__mocks__/
- ads.json · templates/contracts/
- ads.schema.json · templates/contracts/
- 01-consent-eea.yaml · templates/maestro/ads-smoke/
- 02-relaunch.yaml · templates/maestro/ads-smoke/
- 03-break-interstitial.yaml · templates/maestro/ads-smoke/
- 04-rewarded.yaml · templates/maestro/ads-smoke/
- 05-offline.yaml · templates/maestro/ads-smoke/
- 06-geo-other.yaml · templates/maestro/ads-smoke/
- refresh-skadnetwork.ts · templates/scripts/ads/
- skadnetwork-list.test.ts · templates/scripts/ads/
- skadnetwork-list.ts · templates/scripts/ads/
- ad-pacing.test.ts · templates/src/config/
- ad-pacing.ts · templates/src/config/
- admob-ids.test.ts · templates/src/config/
- admob-ids.ts · templates/src/config/
- ads-config.test.ts · templates/src/config/
- ads-config.ts · templates/src/config/
- app-variant.test.ts · templates/src/config/
- app-variant.ts · templates/src/config/
- skadnetwork-ids.ts · templates/src/config/
- result.test.ts · templates/src/core/
- result.ts · templates/src/core/
- use-ad-context.ts · templates/src/hooks/
- use-banner-slot.test.tsx · templates/src/hooks/
- use-banner-slot.ts · templates/src/hooks/
- …and 68 more
SKILL.md (first lines)
# AdMob ads and consent
Makes an Expo app show Google AdMob ads that never interrupt a focused task: Google's consent and
Apple's tracking prompt come first, every "may an ad appear now?" answer comes from pure, tested
functions, testing builds only ever see Google's test ads, iOS and Android each get their own IDs, and
two zero-dependency checkers prove it on the real code.
## Rules that must hold
1. **Ads never interrupt a focused task.** Banners only on the screens contracts/ads.json `bannerSlots`
declares (browsing screens, never a form, editor, dialog, paywall or onboarding), and only while that
screen is focused (`useBannerSlot(screen, facts, useIsFocused())`: stacks and tabs keep it mounted
under pushed screens); interstitials only from the press handler that leaves a natural break the user
just finished, never on mount, app start or during onboarding; rewarded ads only on the user's tap
behind a clear "Watch an ad to ..." label; the AdsPort's show, initialize and preload calls only in
`ad-moments.ts` and `ad-gate.ts`. *Why:* an interruption costs the user's trust, and Google's policies
punish ads that surprise or invite accidental taps.
2. **Every ad decision comes from `ad-policy.ts` / `rewarded-offer.ts`** (pure; numbers from
`src/config/ad-pacing.ts`, at or above contracts/ads.json `pacingFloors`, default 3 breaks before the
first interstitial, then 3 minutes and 2 breaks between two), and every finished natural break is
saved with `recordBreak` where it finishes. *Why:* only pure rules can be tested one by one, the
numbers are configuration, and a history that never counts a break allows exactly one interstitial.
3. **Consent, then Apple's prompt, before any ad request, through the consent moment, and none at all
when ads are off.** UMP refreshes at every launch; where Google's form is required, the app's optional
primer and then the form; once `canRequestAds`, ATT through `ConsentPort.requestTracking()` (iOS only,
while not-determined, the app active); then `setRequestConfiguration` + `initialize` + preload. Only
after onboarding, online, not Premium, over a focused ad screen. `AdsProvider` (through
`consent-moment-flow.ts`) is the only runner of `prepareAds`; banners are allowed only after the ATT
answer and a successful `initialize`, and the adapter mounts its native banner only after it. The
ConsentPort comes from `createConsentPort(adsMode, () => createAdmobConsentAdapter(...))`, so an
`ADS_MODE=off` build never calls UMP or ATT. *Why:* Google requires consent (EEA, UK, Switzerland, US
states), Apple's guideline 5.1.2(i) requires the ATT answer before IDFA use, the SDK may preload at
initialisation, and a UMP update is a network request that would fail the network audit of off builds.
4. **Testing builds never see real ads; real IDs reach the runtime only in production + live builds.**
`resolveAdsMode` decides: development and preview -> `test` (or `off`), production -> `live` (or
`off`); the other pairs throw. IDs live only in `src/config/admob-ids.ts`, read only by
`app.config.ts`; `admobPluginOptions` always gives both platforms an app ID (Google's sample unless
live); `adsExtra` writes units only in live builds; the template's placeholder IDs are noted and
never reach a store build. `expo.extra` is embedded at build time, or replaced by the running EAS
Update's manifest, so `eas update` runs with the channel's environment and the app reads it through
`readAdsExtraForBinary(Application.applicationId)`, which turns ads off for a manifest made for
another variant's app id. *Why:* real ads in a testing build risk the AdMob account; a missing app ID
stops the app at launch.
5. **`react-native-google-mobile-ads` is exactly 17.2.0 and is imported only by `admob-ads-adapter.ts`,
`admob-consent-adapter.ts` and the test-only `admob-consent-debug-adapter.ts`**, with the classic
create/load/show API (no v17 hooks or pools), and the adapters are created only inside
`createAdsPort` / `createConsentPort` in the composition root. *Why:* an upgrade touches three files,
pacing stays in pure code, and pools preload (on Android initialise) before consent.
6. **The reward is granted only on `EARNED_REWARD`, and the AdsPort reports a `RewardedStatus`
(`'loading' | 'ready' | 'unavailable'`), never a loaded flag.** `rewardedOffer` gives a perk the user
waits on `'loading'` and `'hidden'` only when no ad can come, so a flow waiting on it moves on. *Why:*
with a flag, "not loaded" means both "still loading" and "cannot come", and a waiting user is stranded.
7. **Pause media around every fullscreen ad (`runFullscreenAd` with the one `MediaLifecycle`), resume in
`finally`, and make every show settle** on `CLOSED`, on `ERROR` (phase `show`) and on a rejected
`show()`. *Why:* iOS does not background the app for these ads, so nothing else pauses audio or
timers, and a show that never settles leaves media suspended and the handler stuck.
8. **Ads are silent and their failures are values.** Never an error, spinner or "please connect" for
ads; failures go to the error log; branch on `error.phase` then `error.reason`, never `error.code`;
`initialize()` returns a Result; the banner slot has zero height until an ad has loaded. *Why:* an ad
is never what the user came for, and an escaped rejection is dropped silently in release builds.
9. **The audience is declared and enforced, and tracking follows Apple's rules.** contracts/ads.json
`audience`: `general` -> the declared `maxAdContentRating` (default PG), no child tags;
`child-directed` -> G, both child tags, Android's `AD_ID` permission blocked, and no ATT at all (no
tracking package, plugin, purpose text or adapter; `tracking: null`). General audience:
`expo-tracking-transparency` is imported only by `expo-tracking-consent-adapter.ts`, handed to the
consent adapter as `tracking`; its plugin entry gets the source text and every language's catalog has
it; the AdMob plugin gets no `userTrackingUsageDescription`; the console publishes no IDFA explainer.
Declined, restricted or unavailable tracking still serves ads, and nothing is gated on the answer.
*Why:* the rating must match the console, apps for children may not send device data to third
parties (App Review 1.3, Play Families), a missing purpose text crashes, and 5.1.2(i) forbids gating
on ATT.
10. **The SKAdNetwork list is Google's full list of `xxxxxxxxxx.skadnetwork` identifiers, generated and
checked (`--check`) before every release.** *Why:* the plugin copies only what it gets, and Google
changes the list.
11. **Consent reset, the debug geography and the Ad Inspector exist only in test-only code** (the
debug adapter and debug options reached through the test-only entry). *Why:* they change consent
behaviour for real users.
12. **The developer does the AdMob console steps; ask and wait, never invent IDs or publish anything.**
*Why:* the console is the developer's account, and an invented ID or a published setting reaches real
users.
## Workflow
1. **Install.** Read [references/setup-ids-and-modes.md](references/setup-ids-and-modes.md):
`npm install -E react-native-google-mobile-ads@17.2.0` and
`npx expo install expo-tracking-transparency expo-network expo-constants expo-application` (a
child-directed app leaves out `expo-tracking-transparency`).
2. **Copy the templates** at the paths their first lines name (never rewrite them from memory):
`templates/src/services/ads/`, `templates/src/services/consent/admob-consent-debug-adapter.ts` (+ test),
`templates/src/config/{ads-config,admob-ids,ad-pacing,skadnetwork-ids}.ts` (+ tests),
`templates/src/hooks/`, `templates/src/ui/ad-banner-slot.tsx` (+ test), `templates/scripts/ads/`,
`templates/contracts/ads.json` (+ schema; fill the banner screens, audience and floors). The synced
canonical files (`src/services/consent/*`, `src/services/connectivity/*`, `src/config/app-variant.ts`,
`src/core/result.ts`, the two root mocks) are copied only when the app lacks them; a child-directed
app deletes `expo-tracking-consent-adapter.ts` (+ tests) and the tracking mock.
3. **Wire `app.config.ts`** as [examples/app-config-ads.ts](examples/app-config-ads.ts) shows:
`resolveAdsMode`, the two plugin entries, `extra: { ...adsExtra(adsMode, ADMOB_IDS) }`, the tracking
text in every language's `locales` (none of the tracking lines for a child-directed app). With EAS the
next build applies it; locally rerun `npx expo prebuild --clean`. Refresh the native config snapshot
(expo-prebuild-and-config-plugins' checker with `--write`: `APPLICATION_ID` and
`DELAY_APP_MEASUREMENT_INIT` change) and commit it with the plugin change. Publish every later
`eas update` with the channel's environment (`--environment <env>`, `ADS_MODE` unset).
4. **Compose the ports** in the composition root ([examples/create-device-services.ts](examples/create-device-services.ts)):
add `ads`, `consent`, `connectivity`, `fullscreenAd` to `Services`, `createDeviceServices` and
`createTestServices` (architecture-and-boundaries' port recipe), with
`readAdsExtraForBinary(Application.applicationId)` and the consent adapter's `tracking`.
5. **Mount the consent moment.** Read [references/consent-flow.md](references/consent-flow.md); render
`AdsRoot` ([examples/ads-root.tsx](examples/ads-root.tsx)) inside `RootStack` around `AppStack`, with
the app's Premium, onboarding and saved consent facts and, optionally, a primer
([examples/consent-primer.tsx](examples/consent-primer.tsx)). Show the "Ad privacy choices" entry only
while required: the settings model returns `adPrivacy`
([examples/use-settings-model.ts](examples/use-settings-model.ts)), the row is a pure view
([examples/ad-privacy-row.tsx](examples/ad-privacy-row.tsx)).
6. **Place the ads.** Read [references/ad-policy.md](references/ad-policy.md) and
[references/placements-and-lifecycle.md](references/placements-and-lifecycle.md); follow
[examples/use-tasks-model.ts](examples/use-tasks-model.ts) and [examples/tasks-screen.tsx](examples/tasks-screen.tsx):
`useBannerSlot('<screen>', facts, useIsFocused())` in the model hook and `AdBannerSlot` in the view;
`recordBreak` where a break finishes; `showInterstitialIfDue` in the press handler that leaves it;
`useRewardedOffer` and `earnRewardedPerk` for rewards; the `fullscreenAd` gate for every fullscreen ad.From zakariaf/ReactNative-Skills, MIT.
More from zakariaf/ReactNative-Skills
- 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
android-release-play
zakariaf
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
architecture-and-boundaries
zakariaf
Places app code in the src/ layout and checks import boundaries: one-way zones, pure core, ports with fakes, one composition root.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
async-safety
zakariaf
Prevents silent async bugs: effect races, stale results, lost promises, leaked listeners and timers, AbortSignal gaps.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
audio-and-haptics-feedback
zakariaf
Builds SFX and haptics behind ports.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
auth-and-secure-storage
zakariaf
Builds sign-in - tokens in expo-secure-store, shared refresh, protected routes, OAuth PKCE, Apple sign-in.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
background-tasks
zakariaf
Builds expo-background-task jobs: idempotent, budgeted runs.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
ci-pipeline-and-gates
zakariaf
Builds GitHub Actions CI for Expo apps - verify on every PR, SHA-pinned actions, pinned runners and Node, device lanes, manual EAS releases.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9
Signals
- Stars
- 11
- Forks
- 2
- Open issues
- 0
- Last commit (this folder)
- Oct 17, 2026 (14 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/zakariaf/ReactNative-Skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from zakariaf
- 0.606score
analytics-and-consent
zakariaf
Gates analytics on opt-in consent: no SDK before a yes, typed events without personal data, ATT.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
accessibility-as-code
zakariaf
Enforces a11y labels, 44pt targets, 200% text, VoiceOver, TalkBack.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
canvas-gestures-and-hit-testing
zakariaf
Turns canvas taps, swipes, drags and aim into typed intents: one gesture module, hit-testing on the drawn layout.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
adaptive-layout
zakariaf
Lays screens out by window, not device: breakpoints, master-detail panes, readable widths, grids.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
debug-menu-and-test-hooks
zakariaf
Gates test-only code behind one literal TEST_ONLY switch: debug menu, debug link, simulated clock and offline.
SkillAFreshness: A★ 11Claude Code +9 - 0.606score
android-release-play
zakariaf
Ships Android App Bundles to Google Play: upload key, Play App Signing, AAB gate, versionCode, tracks, rollout.
SkillAFreshness: A★ 11Claude Code +9
Done for you
Rather not set it up yourself?
Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.
See how we build AI agents →Read the case studies
Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.
- launches shipped
- 50+
- launches shipped
- media placements
- 500+
- media placements
- founders on every account
- 2
- founders on every account
Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block
Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.
Get admob-ads-and-consent running in your business
Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.
Cite this page
admob-ads-and-consent skill. Agent Mart. https://agentmart.dev/skills/zakariaf-admob-ads-and-consent (last updated 2026-10-24)