Open7

Next13 (Prebid.js + Google Publisher Tag)

katsuo55katsuo55

1. Google Publisher Tagの型定義をinstallする

Google Publisher Tagは window.googletag 変数を使用します。

npm install --save-dev @types/google-publisher-tag
katsuo55katsuo55

2. Google Publisher Tagのロードを実装する

/app/components/ad/gpt.tsx
"use client";
import Script from "next/script";

export const GptHead: React.FC = () => {
  return (
    <>
      <Script
        async
        src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"
        strategy="afterInteractive"
      />
      <Script
        id="gpt-head"
        dangerouslySetInnerHTML={{
          __html: `
            window.googletag = window.googletag || { cmd: [] };
            window.googletag.cmd.push(function() {
              window.googletag.pubads().disableInitialLoad();
              googletag.enableServices();
            });
          `,
        }}
      />
    </>
  );
};
katsuo55katsuo55

3. Prebid.jsのロードを実装する

app/components/ad/prebid.tsx
"use client"
import Script from "next/script";

export const PrebidHead: React.FC = () => {
  return (
    <>
      <Script
        async
        src="https://cdn.jsdelivr.net/npm/prebid.js@latest/dist/not-for-prod/prebid.js"
        strategy="afterInteractive"
      />
      <Script
        id="prebid-head"
        dangerouslySetInnerHTML={{
          __html: `window.pbjs = window.pbjs || { que: [] };`,
        }}
      />
    </>
  );
};
katsuo55katsuo55

4. 2と3のcomponentを読み込む

layout.tsにcomponentのimport処理を追加する。

katsuo55katsuo55

5. Header Biddingするコンポーネントを追加する

app/components/ad/slot.tsx
"use client";

import { usePathname, useSearchParams } from "next/navigation";
import { useEffect } from "react";

export type GptSlot = {
  divId: string;
  adUnitPath: string;
  sizes: googletag.GeneralSize;
};

type PrebidSlot = {
  code: string;
  mediaTypes: {
    banner: {
      sizes: [number, number][];
    };
  };
  bids: {
    bidder: string;
    params: object;
  }[];
};

type SlotsProps = { slots: GptSlot[] };

export const AdSlot = (props: SlotsProps) => {
  const gptSlots = props.slots;
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    googletag.cmd.push(function () {
      const divIds = gptSlots.map((gptSlot) => gptSlot.divId);
      const removeSlots = googletag
        .pubads()
        .getSlots()
        .filter((slot) => {
          return divIds.includes(slot.getSlotElementId());
        });
      if (destroySlots.length > 0) {
        // ページ遷移時にslotを初期化する(destroyする)
        destroySlots(removeSlots);
      }

      const slots = gptSlots.map((gptSlot) => {
        const slot = googletag
          .defineSlot(gptSlot.adUnitPath, gptSlot.sizes, gptSlot.divId)
          ?.addService(googletag.pubads());
        return slot;
      });

      // @ts-ignore
      pbjs.que.push(() => {
        // @ts-ignore
        pbjs.requestBids({
          // Prebidの枠情報
          adUnits: getPrebidSlots(gptSlots),
          // タイムアウト値
          timeout: 2000,
          // Prebidオークション終了後のcallback関数
          bidsBackHandler: function () {
            // @ts-ignore
            pbjs.setTargetingForGPTAsync(divIds);
            googletag.pubads().refresh(slots as googletag.Slot[]);
            divIds.forEach((divId) => googletag.display(divId));
          },
        });
      });
    });
  }, [pathname, searchParams]);
  return <></>;
};

function destroySlots(slots: googletag.Slot[]) {
  googletag.cmd.push(function () {
    googletag.destroySlots(slots);
  });
}

function getPrebidSlots(slots: GptSlot[]): PrebidSlot[] {
  const divIds = slots.map((slot) => slot.divId);
  // SSPのIDを管理する変数
  const allSlots: PrebidSlot[] = [
    {
      code: "div-1",
      mediaTypes: {
        banner: {
          sizes: [
            [300, 250],
            [300, 600],
          ],
        },
      },
      bids: [
        {
          bidder: "appnexus",
          params: {
            placementId: 13144370,
          },
        },
      ],
    },
    {
      code: "div-2",
      mediaTypes: {
        banner: {
          sizes: [
            [728, 90],
            [970, 250],
          ],
        },
      },
      bids: [
        {
          bidder: "appnexus",
          params: {
            placementId: 13144370,
          },
        },
      ],
    },
  ];

  return allSlots.filter((slot) => divIds.includes(slot.code));
}
katsuo55katsuo55

6. 5のcomponentをインポートする

page.tsにimport処理を追加する。

7. 広告を表示するdivタグを追加する