<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: VibeBerry</title>
    <description>The latest articles on DEV Community by VibeBerry (@vibeberry).</description>
    <link>https://dev.to/vibeberry</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3076226%2Fcfd003b5-3999-4643-840f-977c8b50e718.png</url>
      <title>DEV Community: VibeBerry</title>
      <link>https://dev.to/vibeberry</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vibeberry"/>
    <language>en</language>
    <item>
      <title>How We Integrated Google AdSense into a Next.js App Router Project (the Right Way)</title>
      <dc:creator>VibeBerry</dc:creator>
      <pubDate>Tue, 22 Apr 2025 18:32:23 +0000</pubDate>
      <link>https://dev.to/vibeberry/how-we-integrated-google-adsense-into-a-nextjs-app-router-project-the-right-way-4540</link>
      <guid>https://dev.to/vibeberry/how-we-integrated-google-adsense-into-a-nextjs-app-router-project-the-right-way-4540</guid>
      <description>&lt;p&gt;Google AdSense is one of the easiest ways to monetize a website. But if you’re using &lt;strong&gt;Next.js with the App Router&lt;/strong&gt;, setting it up properly requires a few key steps.&lt;/p&gt;

&lt;p&gt;Here’s how we added AdSense to &lt;a href="//vibeberry.io"&gt;vibeberry.io&lt;/a&gt; - with full control, clean architecture, and production-ready integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Create an AdSense Account&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Start by signing up at &lt;a href="//adsense.google.com/start"&gt;adsense.google.com/start&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You’ll need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;enter your domain (must be live and working)&lt;/li&gt;
&lt;li&gt;fill out basic business or personal info&lt;/li&gt;
&lt;li&gt;wait for Google to review your site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you’re in, AdSense will ask you to verify your site by adding a script and an &lt;code&gt;ads.txt&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. Add the Script and ads.txt&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Create the Script Component&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We created a reusable AdSense script loader and placed it here:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/components/third-parties/GoogleAdsense.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { FC } from 'react';
import Script from 'next/script';

const GoogleAdsense: FC&amp;lt;{ pId: string }&amp;gt; = ({ pId }) =&amp;gt; (
  &amp;lt;Script
    async
    src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-${pId}`}
    crossOrigin="anonymous"
    strategy="afterInteractive"
  /&amp;gt;
);

export default GoogleAdsense;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Load It in Production via a Centralized Service Component&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To manage all third-party scripts cleanly, we created a wrapper component:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/components/third-parties/RootLayoutServices.tsx&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
It loads services like AdSense, Analytics, Speed Insights, etc. - &lt;strong&gt;but only in production:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const RootLayoutServices: FC = () =&amp;gt; {
  return process.env.NODE_ENV === 'production' ? (
    &amp;lt;&amp;gt;
      &amp;lt;GoogleAdsense pId="1234567890123456" /&amp;gt;
      {/* Other third-party services */}
    &amp;lt;/&amp;gt;
  ) : null;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we included it at the bottom of our main layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/layout.tsx

&amp;lt;body&amp;gt;
  {/* app content */}
  &amp;lt;RootLayoutServices /&amp;gt;
&amp;lt;/body&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ This ensures your scripts only run live, never during local development.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Add&lt;/strong&gt; &lt;code&gt;public/ads.txt&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create this file:&lt;br&gt;
&lt;code&gt;public/ads.txt&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
And include:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;google.com, pub-1234567890123456, DIRECT, f08c47fec0942fa0&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
Replace pub-1234567890123456 with your &lt;strong&gt;Publisher ID&lt;/strong&gt;, found under &lt;strong&gt;Account&lt;/strong&gt; → &lt;strong&gt;Account information&lt;/strong&gt; in AdSense.&lt;/p&gt;

&lt;p&gt;This tells Google you’re allowed to show ads on your domain.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;3. Create a Manual Ad Unit&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After your site is approved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;strong&gt;Ads&lt;/strong&gt; → &lt;strong&gt;By ad unit&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;Display Ads&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Set the format (we use &lt;strong&gt;responsive&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Copy the generated data-ad-slot value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We prefer &lt;strong&gt;manual units&lt;/strong&gt; over Auto Ads for better layout control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g3d5tfo9l5etbbtq140.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6g3d5tfo9l5etbbtq140.webp" alt="manual units list" width="720" height="61"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Create a Custom Ad Slot Component
&lt;/h2&gt;

&lt;p&gt;We placed our ad slot component here:&lt;br&gt;
&lt;code&gt;src/components/third-parties/AdSlot.tsx&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use client';

import { useEffect } from 'react';

const AdSlot = ({ slot }: { slot: string }) =&amp;gt; {
  useEffect(() =&amp;gt; {
    try {
      (window.adsbygoogle = window.adsbygoogle || []).push({});
    } catch (e) {
      console.error(e);
    }
  }, []);

  return (
   &amp;lt;ins class="adsbygoogle"
     style="display:block"
     data-ad-client="ca-pub-1234567890123456"
     data-ad-slot={slot}
     data-ad-format="auto"
     data-full-width-responsive="true"
    /&amp;gt;
  );
};

export default AdSlot;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now place &lt;code&gt;&amp;lt;AdSlot slot="1234567890" /&amp;gt;&lt;/code&gt; wherever needed in your UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Handle App Router Navigation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Since we’re using &lt;strong&gt;Next.js App Router&lt;/strong&gt;, AdSense and Analytics won’t automatically detect route changes. We fixed this with:&lt;br&gt;
&lt;code&gt;src/components/third-parties/AnalyticsRouter.tsx&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'use client';

import { useEffect, useRef } from 'react';
import { usePathname } from 'next/navigation';

export default function AnalyticsRouter() {
  const path = usePathname();
  const first = useRef(true);

  useEffect(() =&amp;gt; {
    if (first.current) {
      first.current = false;
      return;
    }

    window.gtag?.('event', 'page_view', {
      page_path: path,
      page_location: window.location.href,
    });
  }, [path]);

  return null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures AdSense/GA4 continue to track page views correctly as users navigate between routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Respect Consent Requirements (EU, US)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you serve users in the &lt;strong&gt;EU&lt;/strong&gt;, &lt;strong&gt;California&lt;/strong&gt;, or other regions with privacy laws, you must obtain &lt;strong&gt;user consent&lt;/strong&gt; before showing &lt;strong&gt;personalized ads&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a Consent Management Platform (CMP)&lt;/li&gt;
&lt;li&gt;Or implement manual logic using &lt;code&gt;gtag('consent', ...)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without consent, Google may block personalized ads or fall back to lower-paying non-personalized ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Console Warning (You Can Ignore It)
&lt;/h2&gt;

&lt;p&gt;During development or even in production, you may see this browser console warning:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fut270356tmxdfysbgce9.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fut270356tmxdfysbgce9.webp" alt="warning preview" width="455" height="42"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s harmless and caused by how Next.js handles scripts. Your ads will still load and work perfectly - you can safely ignore it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Recap&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here’s what we did on &lt;a href="//vibeberry.io"&gt;vibeberry.io&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Registered an AdSense account&lt;/li&gt;
&lt;li&gt;✅ Verified the site with a script and ads.txt&lt;/li&gt;
&lt;li&gt;✅ Created manual display ad units&lt;/li&gt;
&lt;li&gt;✅ Centralized all scripts in RootLayoutServices&lt;/li&gt;
&lt;li&gt;✅ Displayed ads only in production&lt;/li&gt;
&lt;li&gt;✅ Handled route changes with App Router&lt;/li&gt;
&lt;li&gt;✅ Respected consent rules for personalized ads&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Setting up AdSense on a modern Next.js project can feel confusing at first - especially with the App Router and all the privacy requirements. But once you break it down into steps, it’s totally manageable.&lt;/p&gt;

&lt;p&gt;If you have any questions or run into something tricky - drop a comment below. Thanks for reading - and good luck with your integration!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
