Getting approved for Google AdSense is less about inserting a script and more about proving your site is high‑quality, policy‑compliant, and technically sound. This guide focuses on the concrete steps that matter for a Next.js (App Router) site - from content and policy prep to ads.txt, Consent Mode v2, and production‑safe code snippets.
TL;DR
- Have quality content first: 10–15 solid posts/pages, unique, helpful, and original.
- Ship required pages: About, Contact, Privacy Policy, Terms; clear navigation; no broken pages.
- Make it crawlable: No
noindex, workingrobots, sitemap, fast Core Web Vitals, mobile‑friendly. - Publish
ads.txtand set Consent Mode v2. Wait 24–72 hours for propagation. - Add the AdSense script site‑wide using
next/script. Use a client‑only ad unit. - Submit the site in AdSense and keep it online while under review (1–14 days typical).
1) Eligibility and Policy Checklist
Before you apply, verify these:
- Original, human‑written content; no AI spam, no mass‑generated or scraped content.
- Clear site purpose and niche; value beyond directory/aggregator pages.
- Minimum viable content footprint: 10–15 high‑quality posts/pages with internal links.
- Required pages: About, Contact, Privacy Policy (must disclose AdSense/ads), Terms.
- Clean UX: readable typography, contrast, consistent navigation, functional search.
- Technical quality: fast (Core Web Vitals), responsive, accessible; no broken links.
- Policy‑safe: no prohibited content (adult, illegal, pirated, dangerous, clickbait/encouragement to click ads, etc.).
2) Technical SEO + Crawlability (Next.js)
- Ensure production pages do not use
noindex. In the App Router, you typically control bots viaapp/robots.ts. Keepindex: truefor public sections and ensure important pages are discoverable from your homepage. - Provide a sitemap (Next.js supports
app/sitemap.ts) and submit it in Google Search Console. - Remove placeholder lorem ipsum, “coming soon” pages, and thin tag/category pages before applying.
- Fix all client and server errors (404/500), and keep redirects clean.
3) Add ads.txt (required)
Google strongly recommends an ads.txt file to declare authorized sellers. This reduces policy warnings and speeds up monetization accuracy.
Place ads.txt at the site root (https://yourdomain.com/ads.txt). Easiest: add it to public/ads.txt in your Next.js app.
google.com, pub-0000000000000000, DIRECT, f08c47fec0942fa0- Replace
pub-0000000000000000with your own AdSense Publisher ID (formatca-pub-XXXXXXXXXXXXXXX→ use the numeric part only if required by your network; AdSense acceptsca-pub-...form in many cases, but Google’s canonical example uses the numericpub-...). - It can take 24–72 hours for
ads.txtto propagate and warnings to clear.
4) Consent Mode v2 (EEA/UK compliance)
If any traffic comes from the EEA/UK, you must implement Consent Mode v2 and use a CMP or your own consent UI. A minimal default‑denied setup in Next.js (update to granted after user consent):
// app/layout.tsx (excerpt)
import Script from "next/script";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<Script id="consent-default" strategy="beforeInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied'
});
`}
</Script>
{children}
</body>
</html>
);
}When a user grants consent in your CMP, update consent to granted at runtime:
// somewhere in your consent banner handler
export const grantAdsConsent = () => {
// @ts-expect-error dataLayer injected globally
window.gtag?.('consent', 'update', {
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
analytics_storage: 'granted',
});
};Use a Google‑certified CMP if you serve EEA/UK. Always reflect the user’s choice accurately.
5) Load AdSense globally (Next.js App Router)
AdSense requires loading its script site‑wide. Use next/script with your client ID.
// app/layout.tsx (excerpt)
import Script from "next/script";
const ADSENSE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID; // e.g., "ca-pub-1234567890123456"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{ADSENSE_CLIENT_ID ? (
<Script
id="adsense-loader"
strategy="afterInteractive"
src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${ADSENSE_CLIENT_ID}`}
crossOrigin="anonymous"
/>
) : null}
{children}
</body>
</html>
);
}Add your variable to .env.local and restart your dev server:
# .env.local
NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID=ca-pub-12345678901234566) Create a safe, reusable AdSense unit (client component)
Use a client component to render ad slots without SSR issues. This pattern prevents hydration mismatches and retries gracefully.
// components/adsense-unit.tsx
"use client";
import { useEffect, useMemo, useRef } from "react";
const ADSENSE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID ?? "";
/**
* Renders a single AdSense slot. Use different slots (data-ad-slot) from your AdSense account.
* Place this component in content pages with meaningful spacing and labels.
*/
export type AdSenseUnitProps = {
slotId: string; // e.g., "1234567890" from your AdSense ad unit
className?: string;
format?: "auto" | "fluid";
fullWidthResponsive?: boolean;
};
export const AdSenseUnit = ({
slotId,
className,
format = "auto",
fullWidthResponsive = true,
}: AdSenseUnitProps) => {
const insRef = useRef<HTMLModElement | null>(null);
const isReady = useMemo(() => typeof window !== "undefined" && !!ADSENSE_CLIENT_ID, []);
useEffect(() => {
if (!isReady || !insRef.current) return;
try {
// @ts-expect-error injected by AdSense script
(window.adsbygoogle = window.adsbygoogle || []).push({});
} catch (err) {
// swallow errors to avoid breaking the page; AdSense may retry
console.error("AdSense push error", err);
}
}, [isReady]);
if (!ADSENSE_CLIENT_ID) return null;
return (
<div aria-label="Advertisement" role="complementary" className={className}>
<ins
ref={insRef as any}
className="adsbygoogle"
style={{ display: "block" }}
data-ad-client={ADSENSE_CLIENT_ID}
data-ad-slot={slotId}
data-ad-format={format}
data-full-width-responsive={fullWidthResponsive ? "true" : "false"}
/>
</div>
);
};Usage example in a page or MDX mapping:
// Example usage in a React page
import { AdSenseUnit } from "@/components/adsense-unit";
export const ArticleWithAds = () => {
return (
<article className="prose mx-auto">
<h1>My Great Article</h1>
<p>Intro content…</p>
<AdSenseUnit slotId="1234567890" className="my-8" />
<p>More content…</p>
</article>
);
};Notes:
- Mark ad containers with accessible labels. Keep ad density reasonable and avoid placing ads too close to interactive elements.
- Each ad unit has its own
slotIdfrom your AdSense dashboard.
7) Submit for AdSense review
- Create/Sign in to AdSense and add your site domain.
- Ensure
ads.txtis live and your site is indexable with solid content and legal pages. - Install the global AdSense script in
app/layout.tsx. - Place at least one ad unit in a visible area on a few content pages.
- Submit for review and keep the site online; avoid large changes during review.
Typical review time: 1–14 days. If denied, you can re‑apply after fixing issues.
8) Common rejection reasons and fixes
- Insufficient content / value: Publish deeper posts (1000–2000+ words), add visuals, fix internal linking.
- Site under construction: Remove “coming soon”, complete navigation, ensure no 404s.
- Policy violations: Remove prohibited content, mature content, click‑bait, or encouragement to click ads.
- Low quality or scraped content: Replace with original work; cite sources; add author info and E‑E‑A‑T signals.
- Poor UX / slow pages: Improve Core Web Vitals, image optimization, font loading, and mobile layout.
- Domain or ownership issues: Verify domain in Search Console; ensure
wwwand apex both resolve. - ads.txt warnings: Publish
ads.txtat the root and wait 24–72 hours.
9) Production hygiene and best practices
- Store IDs in env vars (e.g.,
NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID). Never hardcode in components. - Use a CMP to manage Consent Mode v2; set default denied and update after explicit consent.
- Don’t render ads on pages where they degrade UX (e.g., checkout) - gate rendering by route.
- Avoid showing ads to logged‑in admins while editing (simple user guard).
- Keep ad placements stable; moving them frequently can delay learning and reduce RPM.
10) Quick pre‑submission checklist
- Original content with real value; 10–15+ solid posts.
- Clean navigation; About, Contact, Privacy, Terms live.
- No
noindex; sitemap submitted; no broken links. - Core Web Vitals within good thresholds; mobile responsive.
ads.txtlive at/ads.txtand valid.- Consent Mode v2 implemented (EEA/UK).
- AdSense script installed; at least one visible ad unit placed.
FAQ
How long does approval take? 1–14 days typically; can be faster or slower. Keep the site stable and online.
Can I apply without many posts? You can, but approval odds are much higher with a meaningful content base and a clear site purpose.
Do I need traffic first? Not strictly, but some organic traffic and indexed pages help demonstrate value and compliance.
Will ads.txt errors block approval? Not always, but fixing them reduces warnings and improves monetization quality once approved.
If you follow the content, policy, and technical steps above, AdSense approval for a Next.js site becomes straightforward. Focus on value, keep the site clean and fast, implement ads.txt and Consent Mode correctly, and place ad units conservatively to start.
