Publishers

Ship one component.
Get paid per click.

Your app stays free. One <YeldAd /> goes in a standard slot on each primary screen, styled to match your app. Advertisers pay their bid on valid clicks; you keep 70% of it.

components/app-shell.tsx
import { YeldAd } from "@yeld/react";

<YeldAd
  appId="app_k3j9x2m1p4q8"
  placement="sidebar"
  appearance="plain"
  context={{ page: "editor" }}
/>

What the appId is

Public. Safe to commit. There is no secret key in the client integration, so the component works in a static export, a client bundle, or an open-source repo.

What it costs you

One request per unit on load. No third-party script tags, no advertiser JavaScript, no layout shift. On a no-fill response the component renders nothing.

Install

Three paths in.

Create an app in the dashboard first — you need the app ID before any of these are useful.

Package

Install the SDK, set the server origin, place a unit on each primary screen.

npm install @yeld/react
.env.local
NEXT_PUBLIC_YELD_BASE_URL=https://yeld.dev

Or pass baseUrl as a prop. Props: appId, placement, variant, appearance, theme, context, className, style.

CLI

Installs the package, writes the env var, prints the next step.

npx yeld init
Installing @yeld/react (npm)...
Wrote NEXT_PUBLIC_YELD_BASE_URL
  to .env.local

Done. Next step — add one unit
per primary screen.

Coding agent

Paste one prompt. It carries the coverage model, the placement rules, and your design tokens brief, so the agent does not get to improvise.

agent prompt · 124 lines
Add yeld to this application. You are integrating an ad unit into a free product, so it must be visible, feel native, and be placed where people actually look.

App ID: app_k3j9x2m1p4q8
yeld server: https://yeld.dev
Full spec: https://yeld.dev/llms.txt

Agents can also read /llms.txt directly.

Placement

One unit per screen, on every primary screen.

yeld is prescriptive here, deliberately. A unit that only appears after a user action gets a fraction of the impressions, which makes revenue unpredictable for you and inventory unpredictable for advertisers. It is also the failure mode of handing the job to an agent: left to itself, it buries the unit somewhere real users never reach.

So the model is coverage, not a single slot. Map the screens people actually spend time in, then give each one a unit — a typical app ends up with two to four. One unit at the bottom of one page is an incomplete integration, and it is the version that earns nothing. Work down the slots below and use every one that applies.

1 · variant="card"

sidebar

Card in a persistent sidebar or rail. Highest value — one unit covers every screen.

2 · variant="post-success"

post-action

Beside the result the user just produced. Highest intent. Additive, not a replacement.

3 · variant="card"

in-content

Inline after the first meaningful block of content. Not the end of the page.

4 · variant="compact"

bottom-banner

Full-width below the main content. Fallback only, for screens with no better slot.

If a screen has no natural slot, add an in-content container to it. Do not invent a fifth placement.

Hard requirements

  • One unit per screen, on every primary screen — never two in one viewport
  • At least one unit above the fold on the main screen, on desktop and mobile
  • Renders on page load, in the same position on every visit
  • Never gated behind a user action, an app event, a feature flag, or a non-default tab
  • Reachable without signing in or paying
  • Never in a footer, below the last content block, in a collapsed accordion, or a hidden tab
  • Full width of its container, minimum roughly 280px
  • Never inside a modal, popup, or interstitial, and never blocking the primary action

Screens that never carry a unit

  • Auth screens
  • Checkout and payment flows
  • Error states
  • Empty first-run screens
  • Any surface showing sensitive user data

Context you pass — category, event, page, keywords — can only broaden matching. It never decides whether the unit renders.

Native

It should look like you built it.

The unit ships with no opinion about your visual language. Typography inherits from the host, and every color, radius, and pad is a public CSS custom property, so it can take your tokens instead of ours. Wrap it in your own card component, set appearance="plain" so it stops drawing a second border, and point the accent at whatever your buttons use.

matching the host
<YeldAd
  appId="app_k3j9x2m1p4q8"
  placement="sidebar"
  appearance="plain"
  style={{
    "--yeld-accent": "var(--brand)",
    "--yeld-radius": "8px",
  }}
/>

What you can change

Font, background, text, border, accent, radius, and padding — eleven custom properties, plus three appearance values and a pinned theme for single-theme apps.

What you cannot

The "Sponsored" label and the "Ads by yeld" attribution stay visible. No prop removes them. Blending in is the goal; passing an ad off as your own UI is not, and that protects your users' trust as much as ours.

Relevance

Context is what the slot is worth.

You are paid per click, so a matched ad and a generic one are not the same asset. Tell yeld what the screen is about and the matching improves — no personal data required, and nothing you pass can suppress the unit.

Pick the card variant in high-attention slots and keep compact for dense chrome. Give the unit the same spacing as your other blocks rather than wedging it against your own call to action.

  • Always pass page — which screen this is
  • Pass event at action moments, describing what the user just finished
  • Pass two to five keywords describing what the user is doing there
  • Never personal data, user content, or anything the user typed

Full detail in the conversion section of the docs.

Guarantees

What yeld never does in your app.

Advertisers submit text and an optional logo. yeld renders it. Nothing an advertiser writes reaches the DOM as markup or script, which is why this list can be a guarantee rather than a policy.

  • No third-party JavaScript in your bundle
  • No popups, interstitials, or overlays
  • No autoplay audio or video
  • No unlabeled unit — it adopts your styling, never your identity
  • No UI that imitates your app's own controls or the operating system
  • No fingerprinting, third-party cookies, or cross-site profiles
  • No reading of your users' content

Economics

CPC only. 70/30 split.

Impressions pay nothing, to anyone. The advertiser is charged their bid on a valid click, and the ledger row is written in the same transaction as the click.

Advertiser bid $1.00

$1.00

Charged once, on one valid click.

You earn

$0.70

70% of the click, credited when it is recorded.

yeld keeps

$0.30

30%. That is the entire fee.

No double billing
A second click on the same impression redirects but is never charged again. Unique constraints on the click and the ledger entry enforce it.
Budgets
A campaign stops serving in your app when its total budget is spent. Daily budgets are approximate — they are enforced against spend recorded so far that day.
Your commitment
None. No minimums, no monthly cost, no exclusivity. Remove the component and requests stop.
Payouts
Not live yet. Earnings accrue as an estimated balance on your earnings page; a payout method comes later. This is the honest state of it — read it before you integrate.

Verify

Walk every screen you touched.

Run the app as a new, signed-out user and do these checks on each screen, not just the first one.

1 · signed out
Every screen you placed a unit on renders it without you signing in, paying, or completing a task. If seeing the unit needs any action, the placement is wrong — move it and re-check.
2 · network
A POST to /api/v1/ad fires on page load and returns 200 — without you clicking anything.
3 · render
The sponsored card is in the viewport or within one scroll, and it looks like it belongs to the app. A { fill: false } response that renders nothing is also correct.
4 · above the fold
At least one unit is visible without scrolling on the main screen, on desktop and on mobile.
5 · no doubles
Nothing shifts layout when a unit loads, and no screen shows two units at once.
6 · dashboard
"Check installation" on the app detail page turns green. It counts any valid request for your app ID, including a no-fill, so it confirms wiring rather than inventory.
POST /api/v1/ad
{
  "appId": "app_k3j9x2m1p4q8",
  "placement": "sidebar",
  "context": { "page": "editor" }
}

Reporting

Your dashboard reports impressions, clicks, CTR, earnings, and RPM per app, with a daily trend. Placement keys auto-register, so each slot reports separately and you can see which screens are actually worth anything before you touch them.

Keep it free. Make it pay.

Create an app, copy the ID, add one unit to each primary screen. Nothing to negotiate, nothing to install on your users.