UI Components
Drop-in, shadcnui-compatiable components for signup and referrals you can customize.
Waitlist Forms
CompactWaitlistForm
'use client';
import { Loader2Icon } from 'lucide-react';
import { useId } from 'react';
/**
* Singline line waitlist form containing an email input and a submit button.
*/
export const CompactWaitlistForm = () => {
const emailInputId = useId();
const waitlist = useWaitlist();
return (
<div className="max-w-md">
{waitlist.signUpInfo && <CopyReferralLink referralUrl={waitlist.signUpInfo.referralUrl} />}
{!waitlist.signUpInfo && (
<form className="flex space-x-2" onSubmit={waitlist.handleSubmit}>
<Input
id={emailInputId}
name="email"
placeholder="Your email"
value={waitlist.email}
onChange={waitlist.handleEmailChange}
disabled={waitlist.isSubmitting}
ref={waitlist.emailInputRef}
/>
<Button type="submit" disabled={waitlist.isSubmitting}>
{waitlist.isSubmitting && <Loader2Icon className="mr-2 size-4 animate-spin" />}
Join Waitlist
</Button>
</form>
)}
{waitlist.error && <p className="mt-2 text-sm text-red-600">{waitlist.error}</p>}
</div>
);
};
This component uses CopyReferralLink
component to display the referral link after successful
signup.
WaitlistModal
'use client';
import { Button } from 'components/ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from 'components/ui/dialog';
import { Input } from 'components/ui/input';
import { Loader2Icon } from 'lucide-react';
import { useId } from 'react';
/**
* Waitlist form that opens in a modal dialog.
*/
export const WaitlistModal = () => {
const emailInputId = useId();
const waitlist = useWaitlist();
return (
<Dialog>
<DialogTrigger asChild>
<Button>Request Access</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Request Access</DialogTitle>
<DialogDescription>
We are still refining our product. Join the waitlist to get an early look and help us
shape the future!
</DialogDescription>
</DialogHeader>
{waitlist.isDone && waitlist.signUpInfo && (
<CopyReferralLink referralUrl={waitlist.signUpInfo.referralUrl} />
)}
{!waitlist.isDone && (
<form className="max-w-sm space-y-4 text-center" onSubmit={waitlist.handleSubmit}>
<div className="space-y-2 text-left">
<Input
id={emailInputId}
name="email"
placeholder="Your email"
value={waitlist.email}
onChange={waitlist.handleEmailChange}
disabled={waitlist.isSubmitting}
ref={waitlist.emailInputRef}
/>
{waitlist.error && <p className="text-sm text-red-600">{waitlist.error}</p>}
</div>
<Button type="submit" className="w-full" disabled={waitlist.isSubmitting}>
{waitlist.isSubmitting && <Loader2Icon className="mr-2 h-4 w-4 animate-spin" />}
Join Waitlist
</Button>
</form>
)}
</DialogContent>
</Dialog>
);
};
This component uses CopyReferralLink
component to display the referral link after successful
signup.
Referrals
CopyReferralLink
You're on the list! Share your referral link to jump the queue.
'use client';
import { CheckIcon, CircleCheckBigIcon, CopyIcon } from 'lucide-react';
import { useState } from 'react';
/**
* Displays a success message with a clickable field containing user referral link.
*/
export const CopyReferralLink = ({ referralUrl }: { referralUrl: string }) => {
const [copied, setCopied] = useState(false);
const copyToClipboard = async () => {
await navigator.clipboard.writeText(referralUrl);
if (copied) return; // Prevent multiple copies (e.g. from rapid clicks)
setCopied(true);
setTimeout(() => setCopied(false), 2000); // Reset copied state after 2 seconds
};
return (
<div>
{/* Copy Referral Link */}
<div className="flex items-center">
<Button
variant="outline"
className="block w-full truncate rounded-e-none text-left font-normal text-slate-600"
onClick={copyToClipboard}
>
{referralUrl}
</Button>
<Button
size="icon"
className="flex-shrink-0 rounded-s-none"
onClick={copyToClipboard}
aria-label="Copy to clipboard."
>
{copied ? <CheckIcon className="h-5 w-5" /> : <CopyIcon className="h-5 w-5" />}
</Button>
</div>
{/* Success Message */}
<div className="mt-2 flex space-x-1">
<CircleCheckBigIcon className="h-5 w-5 text-green-500" />
<p className="text-sm text-slate-600">
You're on the list! Share your referral link to jump the queue.
</p>
</div>
</div>
);
};