{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "clerk-waitlist",
  "title": "Clerk Waitlist",
  "description": "Waitlist signup form with email input, animated success state, and queue position display. Built for Clerk Core 3's useWaitlist() hook.",
  "dependencies": [
    "@clerk/nextjs"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/clerk/clerk-waitlist/components/elements/clerk-waitlist.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useWaitlist } from \"@clerk/nextjs\";\n\nfunction cn(...classes: (string | boolean | undefined)[]) {\n  return classes.filter(Boolean).join(\" \");\n}\n\nfunction CheckIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <path d=\"M20 6 9 17l-5-5\" />\n    </svg>\n  );\n}\n\nfunction ArrowRightIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <path d=\"M5 12h14\" />\n      <path d=\"m12 5 7 7-7 7\" />\n    </svg>\n  );\n}\n\nfunction MailIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\" />\n      <path d=\"m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7\" />\n    </svg>\n  );\n}\n\nfunction LoaderIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className={className}\n    >\n      <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n    </svg>\n  );\n}\n\nexport interface ClerkWaitlistProps {\n  className?: string;\n  heading?: string;\n  description?: string;\n  ctaText?: string;\n  successMessage?: string;\n}\n\nexport function ClerkWaitlist({\n  className,\n  heading = \"Get early access\",\n  description = \"Join the waitlist to be the first to know when we launch.\",\n  ctaText = \"Join waitlist\",\n  successMessage = \"You're on the list!\",\n}: ClerkWaitlistProps) {\n  const { waitlist, fetchStatus } = useWaitlist();\n  const [email, setEmail] = React.useState(\"\");\n  const [submitted, setSubmitted] = React.useState(false);\n  const [error, setError] = React.useState<string | null>(null);\n\n  const isLoading = fetchStatus === \"fetching\";\n\n  const handleSubmit = React.useCallback(\n    async (e: React.FormEvent) => {\n      e.preventDefault();\n      if (!email.trim()) return;\n      setError(null);\n\n      const { error } = await waitlist.join({ emailAddress: email });\n      if (error) {\n        setError(error.message ?? \"Something went wrong\");\n        return;\n      }\n      setSubmitted(true);\n    },\n    [email, waitlist],\n  );\n\n  return (\n    <div\n      data-slot=\"clerk-waitlist\"\n      className={cn(\n        \"w-full max-w-sm rounded-xl border border-border bg-card p-6 shadow-sm\",\n        className,\n      )}\n    >\n      {!submitted ? (\n        <div data-slot=\"clerk-waitlist-form\" className=\"space-y-4\">\n          <div data-slot=\"clerk-waitlist-header\" className=\"space-y-1.5\">\n            <h3 className=\"text-lg font-semibold tracking-tight text-foreground\">\n              {heading}\n            </h3>\n            <p className=\"text-sm text-muted-foreground\">{description}</p>\n          </div>\n          <form onSubmit={handleSubmit} className=\"space-y-3\">\n            <div className=\"space-y-1.5\">\n              <div className=\"relative\">\n                <MailIcon className=\"pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground\" />\n                <input\n                  data-slot=\"clerk-waitlist-input\"\n                  type=\"email\"\n                  placeholder=\"you@example.com\"\n                  value={email}\n                  onChange={(e) => {\n                    setEmail(e.target.value);\n                    if (error) setError(null);\n                  }}\n                  required\n                  disabled={isLoading}\n                  className=\"h-10 w-full rounded-lg border border-border bg-background pl-10 pr-3 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50\"\n                />\n              </div>\n              {error && (\n                <p\n                  data-slot=\"clerk-waitlist-error\"\n                  className=\"text-xs text-destructive\"\n                >\n                  {error}\n                </p>\n              )}\n            </div>\n            <button\n              data-slot=\"clerk-waitlist-submit\"\n              type=\"submit\"\n              disabled={isLoading}\n              className=\"inline-flex h-10 w-full items-center justify-center gap-2 rounded-lg bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\"\n            >\n              {isLoading ? (\n                <LoaderIcon className=\"size-4 animate-spin\" />\n              ) : (\n                <>\n                  {ctaText}\n                  <ArrowRightIcon className=\"size-4\" />\n                </>\n              )}\n            </button>\n          </form>\n        </div>\n      ) : (\n        <div\n          data-slot=\"clerk-waitlist-success\"\n          className=\"flex flex-col items-center space-y-3 py-2 text-center\"\n        >\n          <div className=\"flex size-12 items-center justify-center rounded-full bg-green-100 dark:bg-green-950\">\n            <CheckIcon className=\"size-6 text-green-600 dark:text-green-400\" />\n          </div>\n          <div className=\"space-y-1\">\n            <p className=\"text-base font-semibold text-foreground\">\n              {successMessage}\n            </p>\n          </div>\n          <div className=\"w-full rounded-lg bg-muted px-3 py-2\">\n            <p className=\"text-xs text-muted-foreground\">\n              We&apos;ll notify you at{\" \"}\n              <span className=\"font-medium text-foreground\">{email}</span>\n            </p>\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "A clean waitlist form that transitions to a success state with queue position. Designed for Clerk Core 3's new useWaitlist() hook. Customizable heading, description, and CTA.",
  "categories": [
    "clerk"
  ],
  "type": "registry:block"
}