{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "clerk-sign-in",
  "title": "Clerk Sign In",
  "description": "Multi-step sign-in form with email/password and social OAuth buttons. Features step transitions, forgot password link, and clean Clerk aesthetic.",
  "dependencies": [
    "@clerk/nextjs"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/clerk/clerk-sign-in/components/elements/clerk-sign-in.tsx",
      "content": "\"use client\";\n\nimport { useRouter } from \"next/navigation\";\nimport type * as React from \"react\";\nimport { useCallback, useRef, useState } from \"react\";\n\nimport { useClerk, useSignIn } from \"@clerk/nextjs\";\n\nfunction cn(...classes: (string | boolean | undefined)[]) {\n  return classes.filter(Boolean).join(\" \");\n}\n\nfunction GoogleIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n    >\n      <path\n        d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z\"\n        fill=\"#4285F4\"\n      />\n      <path\n        d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\"\n        fill=\"#34A853\"\n      />\n      <path\n        d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\"\n        fill=\"#FBBC05\"\n      />\n      <path\n        d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\"\n        fill=\"#EA4335\"\n      />\n    </svg>\n  );\n}\n\nfunction GitHubIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n    >\n      <path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\" />\n    </svg>\n  );\n}\n\nfunction AppleIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n      fill=\"currentColor\"\n    >\n      <path d=\"M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z\" />\n    </svg>\n  );\n}\n\nfunction ArrowLeftIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    >\n      <path d=\"m12 19-7-7 7-7\" />\n      <path d=\"M19 12H5\" />\n    </svg>\n  );\n}\n\nfunction MailIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\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 EyeIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    >\n      <path d=\"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\" />\n      <circle cx=\"12\" cy=\"12\" r=\"3\" />\n    </svg>\n  );\n}\n\nfunction EyeOffIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    >\n      <path d=\"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\" />\n      <path d=\"M14.084 14.158a3 3 0 0 1-4.242-4.242\" />\n      <path d=\"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\" />\n      <path d=\"m2 2 20 20\" />\n    </svg>\n  );\n}\n\nfunction MicrosoftIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={className}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n    >\n      <rect x=\"2\" y=\"2\" width=\"9.5\" height=\"9.5\" fill=\"#F25022\" />\n      <rect x=\"12.5\" y=\"2\" width=\"9.5\" height=\"9.5\" fill=\"#7FBA00\" />\n      <rect x=\"2\" y=\"12.5\" width=\"9.5\" height=\"9.5\" fill=\"#00A4EF\" />\n      <rect x=\"12.5\" y=\"12.5\" width=\"9.5\" height=\"9.5\" fill=\"#FFB900\" />\n    </svg>\n  );\n}\n\nconst oauthIcons: Record<\n  string,\n  (props: { className?: string }) => React.ReactNode\n> = {\n  oauth_google: GoogleIcon,\n  oauth_github: GitHubIcon,\n  oauth_apple: AppleIcon,\n  oauth_microsoft: MicrosoftIcon,\n};\n\nconst oauthLabels: Record<string, string> = {\n  oauth_google: \"Google\",\n  oauth_github: \"GitHub\",\n  oauth_apple: \"Apple\",\n  oauth_microsoft: \"Microsoft\",\n};\n\nfunction LoaderIcon({ className }: { className?: string }) {\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={cn(\"animate-spin\", className)}\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"16\"\n      height=\"16\"\n      viewBox=\"0 0 24 24\"\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth=\"2\"\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n    >\n      <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n    </svg>\n  );\n}\n\nexport interface ClerkSignInProps {\n  className?: string;\n  showOAuth?: boolean;\n  showSeparator?: boolean;\n  heading?: string;\n  subheading?: string;\n  redirectUrl?: string;\n  signUpUrl?: string;\n  afterSSOUrl?: string;\n}\n\nexport function ClerkSignIn({\n  className,\n  showOAuth = true,\n  showSeparator = true,\n  heading = \"Sign in\",\n  subheading = \"Welcome back\",\n  redirectUrl = \"/\",\n  signUpUrl = \"/sign-up\",\n  afterSSOUrl = \"/sso-callback\",\n}: ClerkSignInProps) {\n  const { signIn, fetchStatus } = useSignIn();\n  const clerk = useClerk();\n  const router = useRouter();\n\n  const isLoading = fetchStatus === \"fetching\";\n\n  const [step, setStep] = useState<\"email\" | \"password\">(\"email\");\n  const [email, setEmail] = useState(\"\");\n  const [password, setPassword] = useState(\"\");\n  const [showPassword, setShowPassword] = useState(false);\n  const [error, setError] = useState<string | null>(null);\n  const [direction, setDirection] = useState<\"forward\" | \"backward\">(\"forward\");\n  const passwordInputRef = useRef<HTMLInputElement>(null);\n\n  // biome-ignore lint/suspicious/noExplicitAny: accessing clerk environment internals for social provider detection\n  const socialStrategies: string[] | undefined = (clerk as any).clerkjs\n    ?.environment?.userSettings?.socialProviderStrategies;\n  const oauthProviders = socialStrategies\n    ? socialStrategies.map((s: string) => ({ strategy: s }))\n    : undefined;\n\n  const handleOAuth = useCallback(\n    async (strategy: string) => {\n      setError(null);\n      const { error } = await signIn.sso({\n        strategy: strategy as \"oauth_google\",\n        redirectUrl,\n        redirectCallbackUrl: afterSSOUrl,\n      });\n      if (error) {\n        setError(error.message ?? \"OAuth sign in failed.\");\n      }\n    },\n    [signIn, afterSSOUrl, redirectUrl],\n  );\n\n  const handleEmailSubmit = useCallback(\n    (e: React.FormEvent) => {\n      e.preventDefault();\n      if (!email.trim()) return;\n      setError(null);\n      setDirection(\"forward\");\n      setStep(\"password\");\n      setTimeout(() => passwordInputRef.current?.focus(), 150);\n    },\n    [email],\n  );\n\n  const handlePasswordSubmit = useCallback(\n    async (e: React.FormEvent) => {\n      e.preventDefault();\n      if (!password.trim()) return;\n      setError(null);\n      const { error } = await signIn.password({ identifier: email, password });\n      if (error) {\n        setError(error.message ?? \"Sign in failed. Please try again.\");\n      } else {\n        router.push(redirectUrl);\n      }\n    },\n    [email, password, signIn, router, redirectUrl],\n  );\n\n  const handleBack = useCallback(() => {\n    setDirection(\"backward\");\n    setStep(\"email\");\n    setPassword(\"\");\n    setShowPassword(false);\n    setError(null);\n  }, []);\n\n  return (\n    <div\n      data-slot=\"clerk-sign-in\"\n      className={cn(\n        \"w-full max-w-sm rounded-xl border border-border bg-card shadow-sm\",\n        className,\n      )}\n    >\n      <div data-slot=\"header\" className=\"px-8 pt-8 pb-2\">\n        <h1\n          data-slot=\"heading\"\n          className=\"text-xl font-semibold tracking-tight text-foreground\"\n        >\n          {heading}\n        </h1>\n        <p\n          data-slot=\"subheading\"\n          className=\"mt-1 text-sm text-muted-foreground\"\n        >\n          {subheading}\n        </p>\n      </div>\n\n      <div data-slot=\"body\" className=\"px-8 pb-8 pt-4\">\n        {showOAuth && step === \"email\" && (\n          <div data-slot=\"oauth-providers\" className=\"flex flex-col gap-2\">\n            {!oauthProviders ? (\n              <>\n                <div className=\"h-10 w-full animate-pulse rounded-lg bg-muted\" />\n                <div className=\"h-10 w-full animate-pulse rounded-lg bg-muted\" />\n              </>\n            ) : (\n              oauthProviders.map(({ strategy }) => {\n                const Icon = oauthIcons[strategy];\n                const label =\n                  oauthLabels[strategy] ?? strategy.replace(\"oauth_\", \"\");\n                if (!Icon) return null;\n                return (\n                  <button\n                    key={strategy}\n                    type=\"button\"\n                    data-slot=\"oauth-button\"\n                    onClick={() => handleOAuth(strategy)}\n                    className=\"flex w-full items-center justify-center gap-2 rounded-lg border border-border bg-background px-4 py-2.5 text-sm font-medium text-foreground transition-colors hover:bg-accent\"\n                  >\n                    <Icon className=\"h-4 w-4\" />\n                    Continue with {label}\n                  </button>\n                );\n              })\n            )}\n          </div>\n        )}\n\n        {showOAuth && showSeparator && step === \"email\" && (\n          <div data-slot=\"separator\" className=\"my-6 flex items-center gap-3\">\n            <div className=\"h-px flex-1 bg-border\" />\n            <span className=\"text-xs font-medium uppercase tracking-wider text-muted-foreground\">\n              or\n            </span>\n            <div className=\"h-px flex-1 bg-border\" />\n          </div>\n        )}\n\n        {error && (\n          <div\n            data-slot=\"error\"\n            className=\"mb-4 rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive\"\n          >\n            {error}\n          </div>\n        )}\n\n        <div data-slot=\"form-container\" className=\"relative overflow-hidden\">\n          <div\n            data-slot=\"step-email\"\n            className={cn(\n              \"transition-all duration-200 ease-in-out\",\n              step === \"email\"\n                ? \"translate-x-0 opacity-100\"\n                : direction === \"forward\"\n                  ? \"-translate-x-full opacity-0 pointer-events-none absolute inset-0\"\n                  : \"translate-x-full opacity-0 pointer-events-none absolute inset-0\",\n            )}\n          >\n            <form onSubmit={handleEmailSubmit}>\n              <div data-slot=\"field\" className=\"flex flex-col gap-1.5\">\n                <label\n                  htmlFor=\"clerk-sign-in-email\"\n                  className=\"text-sm font-medium text-foreground\"\n                >\n                  Email address\n                </label>\n                <div className=\"relative\">\n                  <input\n                    id=\"clerk-sign-in-email\"\n                    type=\"email\"\n                    autoComplete=\"email\"\n                    placeholder=\"you@example.com\"\n                    value={email}\n                    onChange={(e) => setEmail(e.target.value)}\n                    className=\"w-full rounded-lg border border-border bg-background px-3 py-2 pl-9 text-sm text-foreground placeholder:text-muted-foreground/60 outline-none transition-colors focus:border-primary focus:ring-2 focus:ring-primary/20\"\n                  />\n                  <MailIcon className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground/60\" />\n                </div>\n              </div>\n\n              <button\n                type=\"submit\"\n                data-slot=\"continue-button\"\n                disabled={!email.trim()}\n                className={cn(\n                  \"mt-4 w-full rounded-lg bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground transition-colors\",\n                  email.trim()\n                    ? \"hover:bg-primary/90\"\n                    : \"cursor-not-allowed opacity-50\",\n                )}\n              >\n                Continue\n              </button>\n            </form>\n          </div>\n\n          <div\n            data-slot=\"step-password\"\n            className={cn(\n              \"transition-all duration-200 ease-in-out\",\n              step === \"password\"\n                ? \"translate-x-0 opacity-100\"\n                : direction === \"forward\"\n                  ? \"translate-x-full opacity-0 pointer-events-none absolute inset-0\"\n                  : \"-translate-x-full opacity-0 pointer-events-none absolute inset-0\",\n            )}\n          >\n            <form onSubmit={handlePasswordSubmit}>\n              <button\n                type=\"button\"\n                onClick={handleBack}\n                data-slot=\"back-button\"\n                className=\"mb-4 inline-flex items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-foreground\"\n              >\n                <ArrowLeftIcon className=\"h-3.5 w-3.5\" />\n                Back\n              </button>\n\n              <div\n                data-slot=\"user-preview\"\n                className=\"mb-4 flex items-center gap-2 rounded-lg border border-border bg-accent/50 px-3 py-2\"\n              >\n                <div className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-primary/10 text-xs font-medium text-primary\">\n                  {email.charAt(0).toUpperCase()}\n                </div>\n                <span className=\"truncate text-sm text-foreground\">\n                  {email}\n                </span>\n              </div>\n\n              <div data-slot=\"field\" className=\"flex flex-col gap-1.5\">\n                <div className=\"flex items-center justify-between\">\n                  <label\n                    htmlFor=\"clerk-sign-in-password\"\n                    className=\"text-sm font-medium text-foreground\"\n                  >\n                    Password\n                  </label>\n                  <button\n                    type=\"button\"\n                    data-slot=\"forgot-password\"\n                    className=\"text-xs font-medium text-primary transition-colors hover:text-primary/80\"\n                  >\n                    Forgot password?\n                  </button>\n                </div>\n                <div className=\"relative\">\n                  <input\n                    ref={passwordInputRef}\n                    id=\"clerk-sign-in-password\"\n                    type={showPassword ? \"text\" : \"password\"}\n                    autoComplete=\"current-password\"\n                    placeholder=\"Enter your password\"\n                    value={password}\n                    onChange={(e) => setPassword(e.target.value)}\n                    className=\"w-full rounded-lg border border-border bg-background px-3 py-2 pr-9 text-sm text-foreground placeholder:text-muted-foreground/60 outline-none transition-colors focus:border-primary focus:ring-2 focus:ring-primary/20\"\n                  />\n                  <button\n                    type=\"button\"\n                    onClick={() => setShowPassword((prev) => !prev)}\n                    data-slot=\"toggle-password\"\n                    className=\"absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground/60 transition-colors hover:text-foreground\"\n                    tabIndex={-1}\n                  >\n                    {showPassword ? (\n                      <EyeOffIcon className=\"h-4 w-4\" />\n                    ) : (\n                      <EyeIcon className=\"h-4 w-4\" />\n                    )}\n                  </button>\n                </div>\n              </div>\n\n              <button\n                type=\"submit\"\n                data-slot=\"submit-button\"\n                disabled={!password.trim() || isLoading}\n                className={cn(\n                  \"mt-4 flex w-full items-center justify-center gap-2 rounded-lg bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground transition-colors\",\n                  password.trim() && !isLoading\n                    ? \"hover:bg-primary/90\"\n                    : \"cursor-not-allowed opacity-50\",\n                )}\n              >\n                {isLoading ? (\n                  <>\n                    <LoaderIcon className=\"h-4 w-4\" />\n                    Signing in...\n                  </>\n                ) : (\n                  \"Sign in\"\n                )}\n              </button>\n            </form>\n          </div>\n        </div>\n      </div>\n\n      <div data-slot=\"footer\" className=\"border-t border-border px-8 py-4\">\n        <p className=\"text-center text-sm text-muted-foreground\">\n          Don&apos;t have an account?{\" \"}\n          <a\n            href={signUpUrl}\n            data-slot=\"sign-up-link\"\n            className=\"font-medium text-primary transition-colors hover:text-primary/80\"\n          >\n            Sign up\n          </a>\n        </p>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "A beautiful multi-step sign-in form. Step 1 collects email with optional OAuth buttons (Google, GitHub, Apple). Step 2 collects password. Wire up with Clerk's useSignIn() hook.",
  "categories": [
    "clerk"
  ],
  "type": "registry:block"
}