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.
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.
NEXT_PUBLIC_YELD_BASE_URL=https://yeld.devOr 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.
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.
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.
<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
POSTto/api/v1/adfires on page load and returns200— 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.
{
"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.