Stop guessing. Start knowing. The only visibility tracker that knows if your user actually sees the element.
You use IntersectionObserver to track impressions. You are lying to your analytics.
Native observers fail in these common scenarios:
opacity: 0) or visibility: hidden.Real View solves this. It combines IntersectionObserver with DOM Raycasting, Computed Styles, and Page Visibility API to guarantee physical visibility.
requestIdleCallback to prevent main-thread blocking.npm install real-view
# or
pnpm add real-view
# or
yarn add real-view
Use the useRealView hook.
import { useEffect } from 'react'
import { useRealView } from 'real-view/react'
const AdBanner = () => {
const [ref, isVisible] = useRealView({ pollInterval: 1000 })
useEffect(() => {
if (isVisible) console.log("User is ACTUALLY looking at this!")
}, [isVisible])
return <div ref={ref}>Buy Now</div>
}
Use the useRealView composable.
<script setup>
import { ref } from 'vue'
import { useRealView } from 'real-view/vue'
const el = ref(null)
const isVisible = useRealView(el)
</script>
<template>
<div ref="el">
Status: {{ isVisible ? 'SEEN' : 'HIDDEN' }}
</div>
</template>
Use the realView action.
<script>
import { realView } from 'real-view/svelte'
let visible = false;
</script>
<div use:realView={{ onUpdate: (v) => visible = v }}>
I am {visible ? 'visible' : 'hidden'}
</div>
Use the realView directive.
iimport { createSignal } from 'solid-js';
import { realView } from 'real-view/solid';
// Typescript: declare module 'solid-js' { namespace JSX { interface Directives { realView: any; } } }
function App() {
const [visible, setVisible] = createSignal(false);
return (
<div use:realView={{ onUpdate: setVisible }}>
{visible() ? "I see you!" : "Where are you?"}
</div>
);
}
Use the standalone RealViewDirective.
import { Component } from '@angular/core';
import { RealViewDirective } from 'real-view/angular';
@Component({
selector: 'app-tracker',
standalone: true,
imports: [RealViewDirective],
template: `
<div (realView)="onVisibilityChange($event)">
Track Me
</div>
`
})
export class TrackerComponent {
onVisibilityChange(isVisible: boolean) {
console.log('Visibility:', isVisible);
}
}
import { RealView } from 'real-view'
const el = document.querySelector('#banner')
const cleanup = RealView.observe(el, (isVisible) => {
console.log(isVisible ? 'Visible' : 'Hidden')
})
// Later
// cleanup()
You can customize the strictness of the detection.
// React example
useRealView({
threshold: 0.5,
pollInterval: 500,
trackTab: true
})
| Option | Type | Default | Description |
|---|---|---|---|
threshold |
number |
0 |
How much of the element must be in viewport (0.0 - 1.0). |
pollInterval |
number |
1000 |
How often (in ms) to check for occlusion (z-index). |
trackTab |
boolean |
true |
If true, reports false when user switches browser tabs. |
Real View uses a "Lazy Raycasting" architecture to keep performance high:
IntersectionObserver first. If the element is off-screen, the CPU usage is 0%.document.elementFromPoint) at the center of your element. If the ray hits a modal, a sticky header, or a dropdown menu instead of your element, visibility is false.opacity, visibility, and display up the DOM tree."We eliminated the lying
IntersectionObserverreports, saved your analytics from counting impressions hidden behind sticky headers, and absorbed the manual DOM raycasting nightmare. You saved dozens of hours not writing complex visibility logic that would have killed your main thread anyway. Your donation is a fair trade for honest data and weekends free from debugging occlusion."
If this library saved you time, please consider supporting the development:
MIT
visibility viewport intersection occlusion tracking analytics impression react vue svelte angular solid dom monitor viewability