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.
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.
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.
How links are generated
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
| Prop | Type | Default | Description |
|---|---|---|---|
appName | string | "App" | Your product's name, used throughout the card copy. |
appIcon | ReactNode | — | Rendered at the top of the card. |
referralLink | `https://${string}` | — | The invite destination. Required in self-hosted mode, and rejected in cloud mode. |
rewardDescription | string | — | A sentence explaining the offer, shown under the heading. |
referrerRewardType | "monetary" | "coupon" | "in-app" | "other" | — | What the referring user receives. |
referrerRewardAmount | string | — | Free-form amount, e.g. "$20" or "500 credits". |
refereeRewardType | "monetary" | "coupon" | "in-app" | "other" | — | What the invited user receives. |
refereeRewardAmount | string | — | Free-form amount for the invited side. |
expiryHours | number | 24 | How long the offer is advertised as valid. |
showExpiry | boolean | true | Whether the countdown appears on the card. |
Trigger and popup
| Prop | Type | Default | Description |
|---|---|---|---|
buttonLabel | string | "Refer & Earn" | Floating trigger text. |
buttonIcon | ReactNode | — | Replaces 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. |
buttonStyle | CSSProperties | — | Inline styles applied to the trigger. |
popupTitle | string | — | Heading inside the invite card. |
popupWidth | number | 380 | Card width in pixels. |
backdropColor | string | "rgba(0,0,0,0.5)" | Overlay behind the card. |
closeOnBackdropClick | boolean | true | Dismiss the card by clicking outside it. |
visible | boolean | — | Controls whether the widget renders at all. |
defaultOpen | boolean | — | Opens 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.
| Prop | Type | Default | Description |
|---|---|---|---|
showReferralLink | boolean | true | Show the link itself in a copyable box. |
referralLinkLabel | string | "REFERRAL LINK" | Label above the link box. |
copyLinkButtonLabel | string | "Copy Link" | Copy button text. |
copySuccessLabel | string | "Copied!" | Confirmation shown after copying. |
copySuccessDuration | number | 2000 | Milliseconds the confirmation stays visible. |
showShareButton | boolean | true | Show the native share action where available. |
shareButtonLabel | string | "Share" | Share button text. |
shareMessage | string | — | Text 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.
<ReferralWidget
referrerIdentity={{
id: user.id, // your own user id
name: user.name,
email: user.email,
}}
/>| Prop | Type | Description |
|---|---|---|
id | string | Your 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. |
email | string | The referrer's email. Used to resolve the same person across visits when you have no stable id to send. |
name | string | Display name, shown in the invite card. Optional. |
phone | string | Stored 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.
// 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:
// 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
| Prop | Type | Description |
|---|---|---|
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 | () => void | The invite card opened. |
onClose | () => void | The invite card closed. |
onMount | (projectId: string) => void | The widget finished mounting. |
onReferralCreated | (code, identity?) => void | A referral code was generated or fetched for this user. |
onLinkCopied | (link, projectId, identity?) => void | The user copied their link. |
onShare | (projectId, identity?) => void | The user opened the native share sheet. |
onConversionTracked | (event: ConversionEvent) => void | A 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.
<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.
import { ReferralLifecycle } from "@roastnest/react";
function DebugPanel() {
return <ReferralLifecycle />;
}