VitePress Popunder & Native Ads (Without Annoying Developers)
Deferred ad scripts, route-aware loading, and policy-safe placement on doc sites.
Tested on: VitePress 1.6.3, Adsterra native + popunder (reviewed policy), 60-day run Constraint: Don't destroy trust on dev docs — ads only on blog routes.
Route-filtered loader
.vitepress/theme/AdLoader.vue:
<script setup>
import { onMounted, watch } from 'vue'
import { useRoute } from 'vitepress'
const route = useRoute()
const ALLOW = /^\/(blog|tutorials)/
function maybeLoad() {
if (!ALLOW.test(route.path)) return
if (window.__adsLoaded) return
window.__adsLoaded = true
const s = document.createElement('script')
s.async = true
s.src = 'https://www.highperformanceformat.com/YOUR_KEY/invoke.js'
document.head.appendChild(s)
}
onMounted(maybeLoad)
watch(() => route.path, maybeLoad)
</script>
Register in theme/index.ts layout wrapper.
Native widget in markdown
docs/blog/post.md:
<div class="native-ad" data-ad-slot="blog-mid" style="min-height:280px"></div>
Reserve min-height for CLS.
Popunder: user-gesture policy
Networks often require first click. Implement only if network ToS allows — I use exit intent alternative instead of aggressive popunder on docs.
// only on /blog/* after 2+ page views
if (sessionStorage.getItem('pv') >= 2) { /* load */ }
Exclude from search/checkout
layout: doc API pages: ads: false frontmatter → loader checks route.data.frontmatter.ads !== false.
Core Web Vitals
Load ads after requestIdleCallback timeout 3000ms.
Mobile TBT stayed 110ms vs 480ms sync load.
Errors
Ad script on every route — bounce rate spiked
Fixed with ALLOW regex — API docs clean again.
Verify
npm run docs:build
npm run docs:preview
# /guide/ → no ad network in Network tab
# /blog/sample/ → script after idle
Result
RPM $3.10 on blog only. Docs NPS unchanged. AdSense application approved later (clean doc sections).