Toolkit
Beta

UI Components

Drop-in, shadcnui-compatiable components for signup and referrals you can customize.

Waitlist Forms

CompactWaitlistForm

compact_waitlist_form.tsx
'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

waitlist_modal.tsx
'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

You're on the list! Share your referral link to jump the queue.

copy_referral_link.tsx
'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&apos;re on the list! Share your referral link to jump the queue.
        </p>
      </div>
    </div>
  );
};

On this page