Referral widget

A drop-in invite card: a referral link the user can copy or share, the reward on both sides spelled out, and the plumbing to attribute the signups that follow.

Basic setup

In cloud mode the referral code, the link and the reward copy all come from your dashboard, so the widget takes no props at all.

Cloud mode
import { ReferralWidget } from "@roastnest/react";

function InvitePage() {
  return <ReferralWidget />;
}

In self-hosted mode you supply the link and the event handler, and the SDK generates and persists the referral code in localStorage.

Self-hosted mode
import { ReferralWidget } from "@roastnest/react";

function InvitePage() {
  return (
    <ReferralWidget
      mode="self-hosted"
      referralLink="https://myapp.com/invite"
      appName="My Awesome App"
      referrerRewardType="monetary"
      referrerRewardAmount="$20"
      refereeRewardType="monetary"
      refereeRewardAmount="$10"
      onEvent={(payload) => console.log("Referral event:", payload)}
    />
  );
}

The link's domain must match your own

In self-hosted mode referralLink must be an absolute URL whose hostname equals window.location.hostname. If it does not, the widget logs an error and renders nothing rather than handing out links that can never attribute. Cloud mode validates the domain on the dashboard, so the runtime check does not apply.

Self-hosted mode generates a unique code per user on first mount and stores it under roastnest_my_referral_code. It is appended to the link you provide as a query parameter — give it https://myapp.com/invite and users share https://myapp.com/invite?ref=AYUSH123.

Cloud mode fetches both the code and the finished link from the server on mount, so the same user gets the same link across devices.

On the receiving end, the SDK detects the parameter on arrival and persists it, so a visitor who lands on your marketing page and signs up three pages later is still attributed. See Conversion tracking.

Content props

PropTypeDefaultDescription
appNamestring"App"Your product's name, used throughout the card copy.
appIconReactNodeRendered at the top of the card.
referralLink`https://${string}`The invite destination. Required in self-hosted mode, and rejected in cloud mode.
rewardDescriptionstringA sentence explaining the offer, shown under the heading.
referrerRewardType"monetary" | "coupon" | "in-app" | "other"What the referring user receives.
referrerRewardAmountstringFree-form amount, e.g. "$20" or "500 credits".
refereeRewardType"monetary" | "coupon" | "in-app" | "other"What the invited user receives.
refereeRewardAmountstringFree-form amount for the invited side.
expiryHoursnumber24How long the offer is advertised as valid.
showExpirybooleantrueWhether the countdown appears on the card.

Trigger and popup

PropTypeDefaultDescription
buttonLabelstring"Refer & Earn"Floating trigger text.
buttonIconReactNodeReplaces the default gift icon.
buttonMode"icon" | "text" | "both"Whether the trigger shows an icon, a label, or both.
buttonPosition"left-center" | "left-bottom" | "right-center" | "right-bottom" | "bottom-left" | "bottom-right" | "bottom-center""bottom-right"Where the trigger anchors in the viewport.
buttonStyleCSSPropertiesInline styles applied to the trigger.
popupTitlestringHeading inside the invite card.
popupWidthnumber380Card width in pixels.
backdropColorstring"rgba(0,0,0,0.5)"Overlay behind the card.
closeOnBackdropClickbooleantrueDismiss the card by clicking outside it.
visiblebooleanControls whether the widget renders at all.
defaultOpenbooleanOpens the card on mount.

Sharing and copying

The card offers a copy-to-clipboard action and, where the browser supports the Web Share API, a native share sheet.

PropTypeDefaultDescription
showReferralLinkbooleantrueShow the link itself in a copyable box.
referralLinkLabelstring"REFERRAL LINK"Label above the link box.
copyLinkButtonLabelstring"Copy Link"Copy button text.
copySuccessLabelstring"Copied!"Confirmation shown after copying.
copySuccessDurationnumber2000Milliseconds the confirmation stays visible.
showShareButtonbooleantrueShow the native share action where available.
shareButtonLabelstring"Share"Share button text.
shareMessagestringText prefilled into the share sheet alongside the link.

Referrer identity

To tie a referral link to a known user rather than an anonymous visitor, pass referrerIdentity. Nothing to compute and no secret to manage — pass the user you already have.

Identified referrer
<ReferralWidget
  referrerIdentity={{
    id: user.id,        // your own user id
    name: user.name,
    email: user.email,
  }}
/>
PropTypeDescription
idstringYour own user id, exactly as it appears in your database. Comes back on every referral webhook as userId, so you can match a payload to a user without us ever holding their email.
emailstringThe referrer's email. Used to resolve the same person across visits when you have no stable id to send.
namestringDisplay name, shown in the invite card. Optional.
phonestringStored as contact metadata. Optional.

id or email — at least one

Either identifies the referrer on its own. Send both and we resolve by email and record the id alongside, so a later visit carrying only one of the two still lands on the same person and the same referral code. Send neither and the call is rejected with a 400.

Identifying without an email

If you would rather not send us your users' email addresses, don't. Send your own user id and nothing else — we keep a stable person for it, so the same user gets the same referral code on every visit, and every webhook about them carries that id straight back to you.

Identified by user id only
// No email leaves your system.
<ReferralWidget
  referrerIdentity={{
    id: user.id,
    name: user.name,
  }}
/>

The reverse works too, for cases where you have no stable id to hand:

Identified by email only
// No stable id to hand us? Email alone works.
<ReferralWidget
  referrerIdentity={{
    email: user.email,
    name: user.name,
  }}
/>

User ids are scoped to your project

Your id only ever means something inside your own project, so sequential ids like 42 are perfectly safe — they can never collide with another Roastnest customer's user 42.

Identity is taken at face value

Roastnest does not independently verify that a visitor owns the email you send. What gates these calls is your project's site code plus its allowed-origins list, so register every domain the widget runs on under Site Settings.

This matters when Automatic Rewards is on: a qualifying conversion sends a signed referral.reward_granted webhook asking your backend to grant a reward. Treat it as a request to check against your own order and user records — you are the only party that can — rather than a settled fact.

Callbacks

PropTypeDescription
onEvent(payload) => void | Promise<void>Every referral event. Required in self-hosted mode; optional in cloud mode, where it runs alongside the server call.
onOpen() => voidThe invite card opened.
onClose() => voidThe invite card closed.
onMount(projectId: string) => voidThe widget finished mounting.
onReferralCreated(code, identity?) => voidA referral code was generated or fetched for this user.
onLinkCopied(link, projectId, identity?) => voidThe user copied their link.
onShare(projectId, identity?) => voidThe user opened the native share sheet.
onConversionTracked(event: ConversionEvent) => voidA conversion was recorded for an incoming referral.

Custom rendering

If the stock card does not fit, replace either half of it. renderTrigger swaps the floating button and renderCard swaps the card body, both receiving the state and actions they need as arguments. customCSS injects a stylesheet scoped to the widget for smaller adjustments.

Custom trigger
<ReferralWidget
  renderTrigger={({ open, isOpen }) => (
    <button onClick={open} disabled={isOpen}>
      Invite a friend
    </button>
  )}
/>

Lifecycle visualizer

ReferralLifecycle renders a stage-by-stage view of the referral flow — shared, clicked, saved, action, attributed, reward approved — and lets you step through it without running real traffic. It is a development aid; leave it out of production builds.

DebugPanel.tsx
import { ReferralLifecycle } from "@roastnest/react";

function DebugPanel() {
  return <ReferralLifecycle />;
}