Add the BannerX banner to your mini app
BannerX is a banner-exchange network for World App mini apps. Your app shows other apps' banners and earns 1 credit for every valid 60-second view — each credit pays for one view of your own banner across the network. Follow the steps below to go live.
Before you start
Do this once in the BannerX dashboard before touching any code.
- 1Open the dashboard and connect your World ID (Orb verification).
- 2Register your mini app: app name + your World App ID (app_…) from the World Developer Portal. The banner opens your app inside World App through a deep link.
- 3Upload your banner — 1500×120 recommended, PNG / JPEG / WEBP up to 1 MB.
- 4Wait for approval. You receive 100 welcome credits the first time your app is approved.
- 5Copy your App ID from the “My apps” list (the copy button next to each app). You'll paste it into the component.
Install the package
In your mini app project, install the BannerX client from npm. Requires React 18+.
npm install bannerx-client@latestAlways installs the latest published version.
Add the component
The component does not handle login — your app is responsible for authentication and must pass a stable user id in viewerId. Without viewerId the banner is not shown. It renders a fixed 60px bar (bottom by default) and adds a spacer so it never covers your content.
import { BannerX } from "bannerx-client";
export default function App() {
const user = useMyAuth(); // your own login (e.g. MiniKit walletAuth)
return (
<>
{/* the rest of your app */}
{user && (
<BannerX
appId="YOUR_APP_ID" // from the "My apps" list
viewerId={user.walletAddress} // stable user id
/>
)}
</>
);
}Get viewerId from MiniKit (inside World App)
Use the wallet address from MiniKit walletAuth as a stable viewerId — it's the same across every mini app, so views are counted correctly.
import { MiniKit } from "@worldcoin/minikit-js";
const res = await fetch("/api/nonce");
const { nonce } = await res.json();
const { finalPayload } = await MiniKit.commandsAsync.walletAuth({ nonce });
if (finalPayload.status === "success") {
setViewerId(finalPayload.address.toLowerCase());
}Component props
Only appId and viewerId are required.
| Prop | Type | Required | Description |
|---|---|---|---|
| appId | string | Yes | Your App ID registered in BannerX (this is the app that earns credits). |
| viewerId | string | Yes | Stable id of the user logged in to your app (e.g. wallet address or nullifier hash). |
| apiUrl | string | No | Base URL of the BannerX API. Defaults to the production API — you usually don't need to set it. |
| position | "top" | "bottom" | No | Position of the 60px bar. Defaults to "bottom". |
| className | string | No | Extra CSS class on the container. |
| style | CSSProperties | No | Extra inline styles on the container. |
| onExposure | (exposureId) => void | No | Called when a 60s exposure is credited. |
| onError | (error) => void | No | Called on fetch/report errors. |
How it works
- The component fetches a banner from the network (never your own — your appId is excluded) and counts 60 seconds of visible time, pausing when the tab/app is in the background.
- At 60s it records the exposure: the banner's app pays 1 credit and your app receives 1 credit. Then it rotates to the next banner.
- If the network has no banners with credits, it shows the BannerX banner (fallback) — no credits involved.
- The server enforces a minimum 60s interval per user + banner and checks that the host appId is registered and active. Invalid exposures don't earn credits.
Banner not showing?
- viewerId is empty — render <BannerX /> only after your user is logged in.
- Your app isn't approved yet, or it's paused in the dashboard.
- Wrong App ID — copy it again from the “My apps” list (it's the App ID, not your World App ID).
- You only see the BannerX fallback banner when there are no other banners with credits to serve — that's expected.