@revinel/react
React bindings for Revinel publisher integrations. Covers both integration jobs:
- Render ads on your site —
RevinelProvider+ hooks, on top of@revinel/sdk. - Acquire advertisers — the
<TierSelector>/<TierSelectorDialog>"Subscribe to advertise" widget.
Install
npm install @revinel/react
react (>=18) is a peer dependency.
SSR note.
useAd/useAdsfetch on the client — fine for client-only apps, but it means a layout shift and no server-rendered ad. In an SSR framework (Next.js, Remix, TanStack Start), fetch on the server with@revinel/sdk'sgetAdand render the markup yourself; useuseTrackingon the client for impressions/clicks.
Render ads
Wrap your app once, then read ads with the hooks:
import { RevinelProvider, useAd, useTracking } from "@revinel/react"
const AdSlot = () => {
const { data: ad } = useAd({ weightGte: 2.5 }) // premium placement
const { impressionRef, getClickProps } = useTracking(ad?.id)
if (!ad) return null // no eligible ad — render nothing
return (
<a ref={impressionRef} href={ad.websiteUrl} {...getClickProps()}>
{ad.name}
</a>
)
}
export const Ads = () => (
<RevinelProvider workspaceId="your-workspace-id">
<AdSlot />
</RevinelProvider>
)
useTracking takes the ad id (or anything carrying one — the full ad, your own render
shape), records a viewable impression (via IntersectionObserver), and wires click
tracking through getClickProps() (which also tracks middle-click / open-in-new-tab).
Rendering multiple slots? Use one
useAds({ count: n }), notn × useAd(). EachuseAd()sends the same request, so the shared edge cache returns the same ad every time — you'd render duplicates and double-count impressions.useAds({ count })returnsndistinct ads in one call; passexcludeIdsto dedupe against ads fetched elsewhere.
Type ad.meta once via @revinel/sdk's RevinelMetaRegistry (see its README) and every
hook is typed with no per-call generic. If impressionRef can't sit on your ad element
(e.g. a Slot/asChild wrapper that swallows refs), put it on a wrapping element or an
absolutely-positioned sentinel inside the ad.
Tier selector (acquire advertisers)
workspaceId (and optional appUrl) are inherited from RevinelProvider, so inside one
the widget takes no config props (theme defaults to "auto"):
import { TierSelector } from "@revinel/react"
;<TierSelector onCheckout={e => {}} />
Or as a controlled modal:
import { TierSelectorDialog } from "@revinel/react"
const [open, setOpen] = useState(false)
;<TierSelectorDialog open={open} onClose={() => setOpen(false)} />
Outside a provider, pass workspaceId (and appUrl for self-hosted) directly:
<TierSelector workspaceId="your-workspace-id" />.
Build your own pricing UI instead with useTiers() + useCheckout():
import { parseTierFeature, useCheckout, useTiers } from "@revinel/react"
const { data: tiers } = useTiers()
const { redirectToCheckout, isPending } = useCheckout()
// tiers[].features → parseTierFeature(line) → { type, label }
// redirectToCheckout({ tierPriceId }) creates the session and navigates to Stripe
Exports
- Provider:
RevinelProvider,useRevinelClient,useRevinelConfig - Ads:
useAd,useAds,useTracking - Tiers/checkout:
useTiers,useCheckout,parseTierFeature - Widgets:
TierSelector,TierSelectorDialog
Every hook's option and return types are exported (RevinelAdOptions, RevinelQueryState, …)
so you can type wrappers around them.
API reference
Full reference and guides: revinel.com/docs. The OpenAPI
spec is served at /v1/openapi.json on the Revinel API.