{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "clerk-oauth-buttons",
  "title": "Clerk OAuth Buttons",
  "description": "Social login button group with Google, GitHub, Apple, and Microsoft. Supports default, outline, and icon-only variants with horizontal/vertical layouts.",
  "dependencies": [
    "@clerk/nextjs"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/clerk/clerk-oauth-buttons/components/elements/clerk-oauth-buttons.tsx",
      "content": "\"use client\";\n\nimport type * as React from \"react\";\nimport { 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 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\nfunction GoogleLogo({ 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      className={className}\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 GitHubLogo({ 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=\"currentColor\"\n      className={className}\n    >\n      <path d=\"M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2z\" />\n    </svg>\n  );\n}\n\nfunction AppleLogo({ 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=\"currentColor\"\n      className={className}\n    >\n      <path d=\"M17.05 20.28c-.98.95-2.05.88-3.08.4-1.09-.5-2.08-.48-3.24 0-1.44.62-2.2.44-3.06-.4C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z\" />\n    </svg>\n  );\n}\n\nfunction MicrosoftLogo({ 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      className={className}\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\ntype Provider = \"google\" | \"github\" | \"apple\" | \"microsoft\";\ntype Variant = \"default\" | \"outline\" | \"icon\";\ntype Size = \"sm\" | \"default\" | \"lg\";\n\nconst providerConfig: Record<\n  Provider,\n  { label: string; icon: (props: { className?: string }) => React.ReactNode }\n> = {\n  google: { label: \"Google\", icon: GoogleLogo },\n  github: { label: \"GitHub\", icon: GitHubLogo },\n  apple: { label: \"Apple\", icon: AppleLogo },\n  microsoft: { label: \"Microsoft\", icon: MicrosoftLogo },\n};\n\nconst oauthStrategyMap = {\n  google: \"oauth_google\",\n  github: \"oauth_github\",\n  apple: \"oauth_apple\",\n  microsoft: \"oauth_microsoft\",\n} as const;\n\nconst sizeClasses: Record<\n  Size,\n  { button: string; icon: string; iconOnly: string }\n> = {\n  sm: { button: \"h-9 px-3 text-sm gap-2\", icon: \"size-4\", iconOnly: \"size-9\" },\n  default: {\n    button: \"h-10 px-4 text-sm gap-2.5\",\n    icon: \"size-5\",\n    iconOnly: \"size-10\",\n  },\n  lg: {\n    button: \"h-11 px-5 text-base gap-3\",\n    icon: \"size-5\",\n    iconOnly: \"size-11\",\n  },\n};\n\nexport interface ClerkOauthButtonsProps {\n  className?: string;\n  providers?: Provider[];\n  variant?: Variant;\n  layout?: \"horizontal\" | \"vertical\";\n  size?: Size;\n  redirectUrl?: string;\n  afterSSOUrl?: string;\n}\n\nexport function ClerkOauthButtons({\n  className,\n  providers = [\"google\", \"github\", \"apple\", \"microsoft\"],\n  variant = \"default\",\n  layout = \"vertical\",\n  size = \"default\",\n  redirectUrl = \"/\",\n  afterSSOUrl = \"/sso-callback\",\n}: ClerkOauthButtonsProps) {\n  const { signIn, fetchStatus } = useSignIn();\n  const clerk = useClerk();\n  const [loadingProvider, setLoadingProvider] = useState<Provider | null>(null);\n  const sizeConfig = sizeClasses[size];\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 enabledProviders = socialStrategies\n    ? providers.filter((provider) =>\n        socialStrategies.includes(oauthStrategyMap[provider]),\n      )\n    : undefined;\n\n  const handleOAuth = async (provider: Provider) => {\n    setLoadingProvider(provider);\n    const strategy = oauthStrategyMap[provider];\n    const { error } = await signIn.sso({\n      strategy,\n      redirectUrl,\n      redirectCallbackUrl: afterSSOUrl,\n    });\n    if (error) {\n      setLoadingProvider(null);\n    }\n  };\n\n  if (!enabledProviders) {\n    return (\n      <div\n        data-slot=\"clerk-oauth-buttons\"\n        className={cn(\n          \"flex w-full gap-2\",\n          layout === \"vertical\" && \"flex-col\",\n          layout === \"horizontal\" &&\n            variant === \"icon\" &&\n            \"flex-row justify-center\",\n          layout === \"horizontal\" &&\n            variant !== \"icon\" &&\n            \"flex-col sm:flex-row\",\n          className,\n        )}\n      >\n        {providers.map((provider) => (\n          <div\n            key={provider}\n            className={cn(\n              \"animate-pulse rounded-lg bg-muted\",\n              variant === \"icon\" ? sizeConfig.iconOnly : \"h-10 w-full\",\n            )}\n          />\n        ))}\n      </div>\n    );\n  }\n\n  if (enabledProviders.length === 0) return null;\n\n  return (\n    <div\n      data-slot=\"clerk-oauth-buttons\"\n      className={cn(\n        \"flex w-full gap-2\",\n        layout === \"vertical\" && \"flex-col\",\n        layout === \"horizontal\" &&\n          variant === \"icon\" &&\n          \"flex-row justify-center\",\n        layout === \"horizontal\" && variant !== \"icon\" && \"flex-col sm:flex-row\",\n        className,\n      )}\n    >\n      {enabledProviders.map((provider) => {\n        const config = providerConfig[provider];\n        const Icon = config.icon;\n        const isLoading = loadingProvider === provider;\n        const isDisabled = loadingProvider !== null && !isLoading;\n\n        if (variant === \"icon\") {\n          return (\n            <button\n              key={provider}\n              data-slot=\"clerk-oauth-button\"\n              data-provider={provider}\n              type=\"button\"\n              disabled={isDisabled}\n              onClick={() => handleOAuth(provider)}\n              className={cn(\n                \"inline-flex items-center justify-center rounded-lg border border-border bg-card text-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n                sizeConfig.iconOnly,\n              )}\n            >\n              {isLoading ? (\n                <LoaderIcon className={sizeConfig.icon} />\n              ) : (\n                <Icon className={sizeConfig.icon} />\n              )}\n            </button>\n          );\n        }\n\n        return (\n          <button\n            key={provider}\n            data-slot=\"clerk-oauth-button\"\n            data-provider={provider}\n            type=\"button\"\n            disabled={isDisabled}\n            onClick={() => handleOAuth(provider)}\n            className={cn(\n              \"inline-flex w-full items-center justify-center rounded-lg font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\n              sizeConfig.button,\n              variant === \"default\" &&\n                \"border border-border bg-card text-foreground hover:bg-accent\",\n              variant === \"outline\" &&\n                \"border-2 border-border bg-transparent text-foreground hover:bg-accent\",\n            )}\n          >\n            {isLoading ? (\n              <LoaderIcon className={sizeConfig.icon} />\n            ) : (\n              <Icon className={sizeConfig.icon} />\n            )}\n            Continue with {config.label}\n          </button>\n        );\n      })}\n    </div>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "docs": "Flexible social login buttons with brand-accurate SVG logos. Three variants (default, outline, icon-only) and two layouts. Wire up with Clerk's useSignIn() OAuth flow.",
  "categories": [
    "clerk"
  ],
  "type": "registry:block"
}