# Referral Page

> A refer-a-friend page: drawn gift cards, a copyable invite link with share buttons, email invites, reward milestones and referrals.

- Type: React template (Templates)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add referral-page`
- Docs: https://wingo-ui.com/templates/referral-page

## Interaction

The hero rises in once on mount beside two drawn Acme gift cards in 3D (the friend's $20 card in front, your $20 credit behind) that tilt into place over a soft glow and straighten a little on hover, with the latest subscribed friend and the next milestone floating around them on wide screens. Copy link copies the personal invite link and turns into a check; Email, X, LinkedIn and WhatsApp open the real share URLs with a prefilled message, Copy message copies that message, and More opens the system share sheet where the browser has one. The checkout code copies on click. In Your rewards the earned amount rolls up when the panel scrolls into view and the milestone track (1 friend $20, 3 a month of Pro, 5 the hoodie, 10 a year of Business) fills up to your count, across on wide screens and down on phones, with reached, next and locked states. Invite by email takes a list of addresses as chips: Enter, comma, semicolon or space adds one, a pasted list splits into chips, Backspace edits the last one, and an invalid, duplicate, already invited or own address is refused with a message. The personal note and the To line update a drawn preview of the email your friend gets. Send invites spins while onInvite runs (or 900 ms without it), then the friends appear at the top of Your referrals as Invited, a toast confirms and a success line shows; a rejection shows an error and keeps the chips. Your referrals filters by status with pill tabs that show counts (arrow keys move between them), shows a table on wide screens and stacked rows on phones, and Resend on an invited friend spins, then reads Sent. An empty list shows a first-run state with a link to the form. How it works, this month's leaderboard with your own rank, the terms in short, the FAQ (one answer open at a time) and the closing band follow. Reduced motion keeps everything still.

## Installation

```bash
npx wingo-ui@latest add referral-page
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/referral-page` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/templates/referral-page.tsx`
- `components/templates/referral-page-data.ts`

npm dependencies: `npm i lucide-react motion`

Wingo UI dependencies (installed automatically):

- [Marketing Section](https://wingo-ui.com/components/marketing-section) ([Markdown](https://wingo-ui.com/components/marketing-section.md))
- [Marketing Header](https://wingo-ui.com/components/marketing-header) ([Markdown](https://wingo-ui.com/components/marketing-header.md))
- [Marketing Footer](https://wingo-ui.com/components/marketing-footer) ([Markdown](https://wingo-ui.com/components/marketing-footer.md))
- [Steps Section](https://wingo-ui.com/components/steps-section) ([Markdown](https://wingo-ui.com/components/steps-section.md))
- [FAQ Section](https://wingo-ui.com/components/faq-section) ([Markdown](https://wingo-ui.com/components/faq-section.md))
- [CTA Section](https://wingo-ui.com/components/cta-section) ([Markdown](https://wingo-ui.com/components/cta-section.md))
- [Animated Counter](https://wingo-ui.com/components/animated-counter) ([Markdown](https://wingo-ui.com/components/animated-counter.md))
- [Avatar](https://wingo-ui.com/components/avatar) ([Markdown](https://wingo-ui.com/components/avatar.md))
- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Copy Button](https://wingo-ui.com/components/copy-button) ([Markdown](https://wingo-ui.com/components/copy-button.md))
- [Empty State](https://wingo-ui.com/components/empty-state) ([Markdown](https://wingo-ui.com/components/empty-state.md))
- [Field](https://wingo-ui.com/components/field) ([Markdown](https://wingo-ui.com/components/field.md))
- [Tabs](https://wingo-ui.com/components/tabs) ([Markdown](https://wingo-ui.com/components/tabs.md))
- [Tag Input](https://wingo-ui.com/components/tag-input) ([Markdown](https://wingo-ui.com/components/tag-input.md))
- [Textarea](https://wingo-ui.com/components/textarea) ([Markdown](https://wingo-ui.com/components/textarea.md))
- [Toast](https://wingo-ui.com/components/toast) ([Markdown](https://wingo-ui.com/components/toast.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
// app/(app)/refer/page.tsx
"use client"

import Link from "next/link"
import { ReferralPage } from "@/components/templates/referral-page"

export default function ReferRoute() {
  return (
    <ReferralPage
      linkComponent={Link}
      user={{ name: "Ryan Cooper", email: "ryan@brightlane.co", link: "https://acme.com/r/ryan-cooper", code: "RYAN20" }}
      ctaTone="brand"
      onInvite={async (emails, note) => {
        const response = await fetch("/api/referrals/invite", { method: "POST", body: JSON.stringify({ emails, note }) })
        if (!response.ok) throw new Error("Could not send the invites")
      }}
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `design?` | `"classic" \| "bold" \| "minimal"` | `"classic"` | The whole page's look. classic follows the layout props: a split hero with the gift cards on the right over a dotted glow; bold: a pill header, a giant stage title on a gradient mesh, rounder panels, step cards, FAQ cards, an image band and a wordmark footer; minimal: a transparent header, a centered stacked hero, no glow or floating chips, outlined flat panels, no gray bands and a simple footer. The *Props overrides still win. |
| `user?` | `ReferralUser` | `"REFERRAL_USER"` | The signed-in person: { name, email, link, code }. The link fills the copy field and every share URL, the code shows under it and on the cards. |
| `referrals?` | `Referral[]` | `"REFERRAL_REFERRALS"` | The starting list: { id, name?, email, status: "invited" \| "signed-up" \| "subscribed", date (ISO) }. Invites sent from the page are added on top. Counts drive the earned total and the milestones. |
| `milestones?` | `ReferralMilestone[]` |  | Rewards by subscribed friends: { id, friends, reward, description, icon? }. Defaults to getReferralMilestones(amount), so the first one names your reward. |
| `leaders?` | `ReferralLeader[]` | `"REFERRAL_LEADERS"` | This month's top referrers: { id, name, company, friends, earned (USD) }. |
| `rank?` | `number` | `23` | Your place on the leaderboard; past the listed leaders it shows as your own row under them. |
| `steps?` | `StepsSectionItem[]` |  | How it works: { icon?, title, description, meta? }. Defaults to getReferralSteps(friendAmount, amount). |
| `terms?` | `string[]` |  | The short version of the program terms. Defaults to getReferralTerms(amount). |
| `faq?` | `FaqItem[]` |  | { question, answer } pairs about referrals. Defaults to getReferralFaq(friendAmount, amount). |
| `labels?` | `Partial<ReferralPageLabels>` | `"DEFAULT_REFERRAL_PAGE_LABELS"` | Every visible string; the ones with amounts or counts in them are functions. |
| `hrefs?` | `Partial<ReferralPageHrefs>` | `"DEFAULT_REFERRAL_PAGE_HREFS"` | { workspace, rewards, invite, terms }: the header button, the in-page anchors and the full terms. |
| `reward?` | `number` | `20` | What you earn per subscribed friend, in USD: the title, the cards, the earned total and the reward column. |
| `friendReward?` | `number` | `20` | What your friend gets off their first month, in USD: the title, the friend's card and the email. |
| `maxInvites?` | `number` | `20` | Emails per send; at the limit the chips field stops taking more. |
| `defaultFilter?` | `"all" \| "invited" \| "signed-up" \| "subscribed"` | `"all"` | The referrals filter on load. |
| `defaultNote?` | `string` | `""` | The personal note on load; empty shows the placeholder in the preview. |
| `showShare?` | `boolean` | `true` | The share buttons under the invite link. |
| `showRewards?` | `boolean` | `true` | Your rewards: the earned total and the milestone track. |
| `showInvite?` | `boolean` | `true` | Invite by email with the email preview. |
| `showReferrals?` | `boolean` | `true` | Your referrals list. |
| `showSteps?` | `boolean` | `true` | How it works. |
| `showLeaderboard?` | `boolean` | `true` | This month's top referrers. |
| `showTerms?` | `boolean` | `true` | The short terms card. |
| `showFaq?` | `boolean` | `true` | The FAQ. |
| `showCta?` | `boolean` | `true` | The closing call to action. |
| `stepsLayout?` | `"horizontal" \| "cards" \| "vertical"` | `"horizontal"` | How it works as a numbered row, numbered tiles or a timeline rail. Applies to the classic design. |
| `ctaVariant?` | `"inverted" \| "soft" \| "split"` | `"inverted"` | The closing band: a solid band, a gray band, or copy beside the buttons on a raised panel. Applies to the classic design. |
| `alternate?` | `boolean` | `true` | Gray bands on every other visible section. Applies to the classic and bold designs. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Accents across the page: the hero glow, the cards' sheen, the milestone fills and nodes, leaderboard bars, terms checks and step markers. |
| `ctaTone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Copy link, Send invites, the phone menu's button and the closing band; brand is the marketing orange. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | The corners of the emails and note fields. |
| `header?` | `boolean` | `true` | The marketing header with Open Acme and your avatar; off inside a site layout. |
| `footer?` | `boolean` | `true` | The marketing footer. |
| `headerProps?` | `Partial<MarketingHeaderProps>` |  | Passed to MarketingHeader. |
| `footerProps?` | `Partial<MarketingFooterProps>` |  | Passed to MarketingFooter. |
| `stepsProps?` | `Partial<StepsSectionProps>` |  | Overrides for the StepsSection. |
| `faqProps?` | `Partial<FaqSectionProps>` |  | Overrides for the FaqSection. |
| `ctaProps?` | `Partial<CtaSectionProps>` |  | Overrides for the CtaSection. |
| `onInvite?` | `(emails: string[], note: string) => (void \| Promise<unknown>)` |  | Send invites. A promise keeps the button loading and a rejection shows an error with the chips kept. Without it the form waits 900 ms and toasts. Either way the friends then appear in Your referrals as Invited. |
| `onResend?` | `(referral: Referral) => (void \| Promise<unknown>)` |  | Resend on an invited friend; a promise keeps it loading. Without it: 900 ms and a toast. |
| `onShare?` | `(channel: "email" \| "x" \| "linkedin" \| "whatsapp" \| "native") => void` |  | A share button was pressed; the share URL still opens. |
| `onCopyLink?` | `(link: string) => void` |  | The invite link was copied. |
| `toastOptions?` | `ToastOptions \| false` | `"{}"` | Options for the pretend send and resend toasts ({ toasterId, duration... }); false sends none. A Toaster must be mounted to see them. |
| `animateIn?` | `boolean` | `true` | The hero rises in once, the cards tilt into place and the earned total rolls up on view; false keeps the page still (thumbnails). Reduced motion always does. |
| `locale?` | `string` | `"en-US"` | Intl locale for the USD amounts, counters and dates. |
| `linkComponent?` | `(props: LinkComponentProps) => ReactNode` | `"<a>"` | The router link for every in-site link on the page (next/link in an app). Share links stay plain anchors. |
| `className?` | `string` |  | Classes for the root. |
| `classNames?` | `Partial<Record<ReferralPageSlot, string>>` |  | Classes for header, hero, title, link, share, cards, rewards, milestones, invite, form, preview, referrals, list, steps, leaderboard, terms, faq, cta and footer. |

## Parts

### REFERRAL_PAGE_SAMPLE (constant)

All the sample content in one object: user, referrals, milestones, leaders, steps, terms and faq.

### REFERRAL_USER (constant)

Ryan Cooper with the link acme.com/r/ryan-cooper and the code RYAN20.

### REFERRAL_REFERRALS (constant)

Eight friends: three subscribed, two signed up and three invited.

### REFERRAL_MILESTONES (constant)

1 friend $20 credit, 3 a month of Pro, 5 the Acme hoodie, 10 a year of Business.

### REFERRAL_LEADERS (constant)

This month's five top referrers.

### REFERRAL_STEPS (constant)

Share your link, your friend gets $20, you get $20.

### REFERRAL_TERMS (constant)

Five lines of program terms.

### REFERRAL_FAQ (constant)

Six questions about timing, the friend's reward, limits, teammates, missing rewards and credit.

### getReferralMilestones (function)

(amount = "$20") => ReferralMilestone[]: the sample milestones with your formatted reward in the first one.

### getReferralSteps (function)

(friendAmount = "$20", amount = "$20") => StepsSectionItem[]: the three steps with both amounts.

### getReferralTerms (function)

(amount = "$20") => string[]: the short terms.

### getReferralFaq (function)

(friendAmount = "$20", amount = "$20") => FaqItem[]: the sample questions with both amounts.

### DEFAULT_REFERRAL_PAGE_LABELS (constant)

The English strings.

### DEFAULT_REFERRAL_PAGE_HREFS (constant)

The default link targets.

### REFERRAL_PAGE_DESIGNS (constant)

The design names, classic first: ["classic", "bold", "minimal"].

### ReferralPageProps (type)

Props of ReferralPage.

### ReferralPageDesign (type)

"classic" | "bold" | "minimal".

### ReferralFilter (type)

"all" | "invited" | "signed-up" | "subscribed".

### Referral (type)

One invited friend.

### ReferralStatus (type)

"invited" | "signed-up" | "subscribed".

### ReferralUser (type)

The signed-in person sharing their link.

### ReferralMilestone (type)

A reward unlocked by a number of subscribed friends.

### ReferralLeader (type)

One leaderboard row.

### ReferralShareChannel (type)

"email" | "x" | "linkedin" | "whatsapp" | "native".

### ReferralPageLabels (type)

The strings labels can override.

### ReferralPageHrefs (type)

The link targets hrefs can override.

## Examples

- **Bold design**: design="bold": a giant stage title on a gradient mesh, rounder panels, step cards, FAQ cards and a wordmark footer.
- **Minimal design**: design="minimal": a transparent header, a centered hero, outlined panels, no gray bands and a simple footer.
- **Give $50, get $25 in brand orange**: friendReward 50 and reward 25 with tone and ctaTone brand: the title, cards, email and totals follow the amounts.
- **First visit, nobody invited yet**: An empty referrals list: no earned total, an empty milestone track and a first-run state that points to the form.
- **Mail service down**: An onInvite that rejects: add a few emails and send to see the error while the chips stay in place.

## See also

- [Affiliate Page](https://wingo-ui.com/templates/affiliate-page) ([Markdown](https://wingo-ui.com/templates/affiliate-page.md))
- [Startups Page](https://wingo-ui.com/templates/startups-page) ([Markdown](https://wingo-ui.com/templates/startups-page.md))
- [Pricing Page](https://wingo-ui.com/components/pricing-page) ([Markdown](https://wingo-ui.com/components/pricing-page.md))
- [Tag Input](https://wingo-ui.com/components/tag-input) ([Markdown](https://wingo-ui.com/components/tag-input.md))
- [Copy Button](https://wingo-ui.com/components/copy-button) ([Markdown](https://wingo-ui.com/components/copy-button.md))

---

Canonical: https://wingo-ui.com/templates/referral-page
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
