Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Adland (@adland/react)

React components for displaying on-chain ads from 0xSlots. Fetches ad data directly from the blockchain (RPC + IPFS) — no centralized backend.

Install

npm install @adland/react

Quick Start

import { Ad, AdImage, AdTitle, AdLoaded, AdEmpty, AdLoading } from "@adland/react";
 
<Ad slot="0xabc...123" chainId={8453}>
  <AdLoaded>
    <AdImage className="size-12 rounded-md" />
    <AdTitle className="text-sm font-medium" />
  </AdLoaded>
  <AdEmpty>Your ad here</AdEmpty>
  <AdLoading>Loading...</AdLoading>
</Ad>

How It Works

Ad mount
  → RPC: getSlotInfo(slot) → module address
  → RPC: getURI(module, slot) → IPFS URI
  → IPFS: fetch(uri) → ad data (type, metadata, image)
  → Render via compound children

All reads are on-chain. Ad content is stored on IPFS. No API keys required.

<Ad> Props

PropTypeDescription
slotstringSlot contract address
chainIdSlotsChainChain ID (default: Base 8453)
dataAdDataStatic data — skips on-chain fetch
rpcUrlstringCustom RPC URL (default: public RPC)
baseLinkUrlstringBase URL for empty-state CTA (default: https://app.0xslots.org)

The <Ad> wrapper handles fetching and provides context to its children. It also renders as a clickable <div> — clicking a loaded ad performs the ad action, clicking an empty ad links to the slot page.

Compound Children

Content (render when ad has data)

ComponentDescription
<AdImage>Ad image. Props: fallback
<AdTitle>Ad title. Props: fallback
<AdDescription>Ad description. Props: fallback
<AdBadge>Type badge with icon + label
<AdLabel>Static label (default: "AD")

State (conditional rendering)

ComponentRenders when...
<AdLoaded>Ad data is available
<AdEmpty>Slot has no ad (default text: "Your ad here")
<AdLoading>Fetching in progress
<AdError>Fetch failed

useAd() Hook

Access ad context from within an <Ad> wrapper:

import { useAd } from "@adland/react";
 
function CustomAdContent() {
  const { data, isLoading, isEmpty, slot } = useAd();
  // ...
}

Ad Types

Ads are typed by their action:

TypeActionData
linkOpens URL{ url }
castOpens Farcaster cast{ hash }
miniappOpens Farcaster miniapp{ url }
tokenViews token{ address, chainId }
farcasterProfileViews profile{ fid }

Click Behavior

StateAction
LoadedPerforms ad action (open link, view cast, etc.)
EmptyOpens ${baseLinkUrl}/slots/${slot}?chain=${chainId}
LoadingNo-op

In a Farcaster miniapp, actions use the native SDK (openUrl, openMiniApp, etc.). On web, falls back to window.open.

Full Example

<Ad
  slot="0xb6a6d14bb374b6aa6d52b2b982547031fddfeed5"
  chainId={8453}
  rpcUrl="https://base-mainnet.g.alchemy.com/v2/YOUR_KEY"
  className="flex items-center gap-3 p-3 border rounded-lg"
>
  <AdLoaded className="flex items-center gap-3 w-full">
    <AdImage className="size-12 rounded-md object-cover" />
    <div>
      <AdTitle className="text-sm font-medium" />
      <AdBadge className="text-xs text-muted-foreground" />
    </div>
  </AdLoaded>
  <AdEmpty className="text-center text-muted-foreground">
    Your ad here
  </AdEmpty>
  <AdLoading className="animate-pulse">
    <div className="size-12 rounded-md bg-muted" />
  </AdLoading>
</Ad>