Today I looked into how to properly detect when an item is actually visible on the screen in React Native, because the current banner tracking I've recently added fires on load even for components that never appear in the viewport. Not great for accurate analytics.
I explored a few directions:
onLayout isn't enough; it triggers even if the item is off-screen.measure() / measureInWindow() to get the element’s positiononScroll to listen for viewport changesI also checked a few libraries (react-native-inviewport, react-native-intersection-observer) but they’ll need performance testing in long lists.
It’s a small detail but for banner impressions, these details matter.
The global standard comes from the IAB (Interactive Advertising Bureau) and like Google, Meta etc. follows as well. An ad impression should be counted only when:
These prevent “fake” impressions and inflated metrics.
A valid impression must meet all:
| Condition | Requirement |
|---|---|
| Visibility | At least 50% on screen |
| Duration | Minimum 1s visible |
| Frequency | Logged once per load |
| Valid state | App active, component actually in viewport |
AdMob and all certified SDKs do this inside the SDK.
Since we’re using custom banners, we have to recreate the logic ourselves.
I built everything around three core hooks opting out an external library:
useInViewuseAdAnalyticsuseCTAAnalyticsAll coordinated through the global useScrollTracking provider. The system now detects real viewport visibility using measureInWindow(), calculates visible-area percentage, enforces the ≥50% visibility rule, and ensures the ad stays visible for 1 continuous second before counting it as a valid impression.
I also added app-state checks (foreground only), fast-scroll detection to block fake impressions, and automatic timer resets any time a condition breaks. Clicks are only logged if a valid impression exists, and everything uses a shared impression_id so multi-partner sheets can link CTA → item clicks cleanly.
Metadata handling is now consistent (placement index/position, partner order, campaign details). Overall, the system mirrors how Google/Meta handle viewability but fully custom and built specifically for our app.
Accurate impressions → correct CTR → honest reporting → trustworthy campaigns.
Now we can have these dashboards in Posthog with impression/click properties → automatic funnels & insights:
I’ll turn this into a full technical case study later, but these are the core notes and base implementation from today.