Skip to content

Repository files navigation

AdInject + Sanity Reference Template

The official reference implementation and example template demonstrating how to integrate adinject-react and adinject-devtools with Next.js 16 App Router and Sanity CMS.


Architecture Overview

flowchart TD
    A[Sanity Content Lake / Studio] -->|Portable Text AST| B[Next.js Server Component]
    B -->|injectPortableTextAds / injectPortableTextAffiliate| C[Enhanced AST with Ad & Affiliate Blocks]
    C -->|@portabletext/react| D[Rendered Article Page]
    D -->|components.types['adinject.adSlot']| E[Zero-CLS AdSenseSlot Frame]
    D -->|components.types['adinject.affiliateCard']| F[Responsive AffiliateCard]
    G[AdInjectProvider] -->|Context Defaults| D
    H[AdInjectDevTools] -->|Visual Inspector & Policy Audit| D
Loading

Key Capabilities Demonstrated

  1. AST-Level Portable Text Ad Injection
    Uses injectPortableTextAds to insert ad blocks (adinject.adSlot) directly into the Sanity Portable Text AST before React renders. Avoids brittle HTML regex parsing, DOM scraping, or client-side hydration flickers.

  2. Affiliate Product Recommendation Embeds
    Uses injectPortableTextAffiliate to dynamically inject high-converting product review cards with FTC disclosures, ratings, and merchant comparisons.

  3. In-Feed Grid Monetization
    Uses <AdInjectFeed /> to interleave responsive ad cards between articles in a blog feed at configurable intervals.

  4. Zero Cumulative Layout Shift (CLS)
    Guarantees 0.00 CLS scores by pre-reserving container heights and aspect ratios with <AdSlotFrame />.

  5. Integrated Visual DevTools & Policy Auditor
    Includes <AdInjectDevTools /> floating dock providing real-time ad slot inspection, placement recommendations, and Google AdSense density policy auditing.


Step-by-Step Integration Guide

1. Install Dependencies

bun add adinject-react @portabletext/react next-sanity
bun add -d adinject-devtools

2. Define the Sanity Schema

Add adSlotType and affiliateCardType to your Sanity Studio schema so that your Portable Text editor can preview and optionally accept manually placed ads:

// src/sanity/schemaTypes/adSlotType.ts
import { defineField, defineType } from "sanity";

export const adSlotType = defineType({
  name: "adinject.adSlot",
  title: "Ad Slot (AdInject)",
  type: "object",
  fields: [
    defineField({ name: "slotId", title: "Slot ID", type: "string" }),
    defineField({ name: "format", title: "Format", type: "string", initialValue: "fluid" }),
  ],
});

Include it in your Portable Text block configuration (blockContentType.ts):

defineArrayMember({ type: "adinject.adSlot" }),
defineArrayMember({ type: "adinject.affiliateCard" }),

3. Build the Portable Text Renderer

Transform the AST before passing it to Sanity's @portabletext/react component:

// src/components/blog/PortableTextRenderer.tsx
"use client";

import { PortableText, type PortableTextComponents } from "@portabletext/react";
import {
  injectPortableTextAds,
  injectPortableTextAffiliate,
  AdSenseSlot,
  AffiliateCard,
  type InjectedAdBlock,
  type InjectedAffiliateBlock,
} from "adinject-react";

export function PortableTextRenderer({ blocks }) {
  // 1. Inject ad slots into Portable Text AST
  const { content: transformedBlocks } = injectPortableTextAds({
    blocks,
    rule: {
      id: "in_article_rule",
      name: "Article Body Ads",
      enabled: true,
      paragraphInterval: 3,        // Insert an ad every 3 paragraphs
      minWordsBeforeFirstAd: 80,   // Wait at least 80 words
      maxAdsPerArticle: 3,         // Maximum 3 ads
    },
    adUnit: {
      id: "slot_article",
      client: "ca-pub-1234567890",
      slot: "9876543210",
      format: "fluid",
      testMode: true,
    },
  });

  // 2. Map custom Sanity types to AdInject components
  const components: PortableTextComponents = {
    types: {
      "adinject.adSlot": ({ value }) => (
        <AdSenseSlot
          client={value.adUnit.client}
          slot={value.adUnit.slot}
          format={value.adUnit.format}
          testMode={value.adUnit.testMode}
        />
      ),
      "adinject.affiliateCard": ({ value }) => (
        <AffiliateCard product={value.data || value} variant="horizontal" />
      ),
    },
  };

  return <PortableText value={transformedBlocks} components={components} />;
}

4. Wrap with AdInjectProvider & AdInjectDevTools

// src/components/monetization/MonetizationProvider.tsx
"use client";

import { AdInjectProvider } from "adinject-react";
import { AdInjectDevTools } from "adinject-devtools";

export function MonetizationProvider({ children }: { children: React.ReactNode }) {
  return (
    <AdInjectProvider
      client="ca-pub-1234567890"
      testMode={true}
    >
      {children}
      <AdInjectDevTools position="bottom-right" />
    </AdInjectProvider>
  );
}

Running the Project

Start Development Server

bun run dev

Run Automated Tests

bun test

Executes the test suite verifying AST idempotency, threshold limits, affiliate card injection, and feed interleaving.

Run Production Build

bun run build

Generates static pages (SSG) with verified Server-Side AST transformations and zero hydration mismatches.


License

MIT © LinuxCTRL

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages