Guide

Next.js

Next.js Integration

Server and client-side rendering options

đź’ˇ Server vs Client Rendering

Server Component (SSR): The iframe renders on the server. Add ?ssr=1 to prevent double-counting views. Views are tracked client-side when the ad actually renders in the browser.

Client Component (CSR): The iframe renders in the browser. Standard tracking applies—each iframe load counts as a view.

⚠️ Always use ?ssr=1 for server-rendered iframes to ensure accurate analytics!

1 Server Component (Recommended)

Use this in any Server Component. No 'use client' directive needed.

// components/BeaverAd.tsx
export default function BeaverAd() {
  return (
    <div className="beaver-ad-container">
      <iframe
        src="https://example.com/ad-slot?ssr=1"
        width={280}
        height={90}
        style={{ border: 'none', overflow: 'hidden' }}
        frameBorder="0"
        scrolling="no"
        loading="lazy"
      />
    </div>
  );
}

Note: The ?ssr=1 parameter tells Beaver.ad to track the view client-side instead of on the server fetch. This prevents SSR builds from inflating view counts.

2 Client Component with Lazy Loading

Use this for dynamic loading and visibility tracking.

'use client';

import { useEffect, useRef, useState } from 'react';

export default function BeaverAdClient() {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.disconnect();
        }
      },
      { rootMargin: '100px' }
    );

    if (containerRef.current) {
      observer.observe(containerRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <div ref={containerRef} className="beaver-ad-container">
      {isVisible && (
        <iframe
          src="https://example.com/ad-slot"
          width={280}
          height={90}
          style={{ border: 'none', overflow: 'hidden' }}
          frameBorder="0"
          scrolling="no"
        />
      )}
    </div>
  );
}

Client-only: No ?ssr=1 needed since the iframe only loads in the browser. Views are automatically tracked correctly.

3 Dynamic Import (Code Splitting)

Load ads asynchronously to reduce initial bundle size.

import dynamic from 'next/dynamic';

const BeaverAd = dynamic(() => import('@/components/BeaverAdClient'), {
  loading: () => <div className="h-24 bg-gray-200 animate-pulse rounded" />,
  ssr: false
});

export default function HomePage() {
  return (
    <main>
      <h1>Welcome</h1>
      <BeaverAd />
    </main>
  );
}
Guides