{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "linked-accounts",
  "title": "Linked Accounts",
  "description": "Display and manage linked social accounts. Shows all linked social accounts with the ability to unlink them, followed by available providers that can be linked.",
  "dependencies": [
    "@better-auth-ui/core@latest",
    "@better-auth-ui/react@latest",
    "better-auth",
    "cn",
    "lucide-react"
  ],
  "registryDependencies": [
    "alert",
    "button",
    "card",
    "dialog",
    "item",
    "skeleton",
    "sonner",
    "spinner"
  ],
  "files": [
    {
      "path": "examples/start-shadcn-example/src/components/auth/reauthentication.tsx",
      "content": "\"use client\"\n\nimport {\n  getReauthenticationSignInURL,\n  isReauthenticationSignInURL\n} from \"@better-auth-ui/core\"\nimport { useAuth, useSignOut } from \"@better-auth-ui/react\"\nimport { useSyncExternalStore } from \"react\"\nimport { Alert, AlertDescription, AlertTitle } from \"@/components/ui/alert\"\nimport { Button } from \"@/components/ui/button\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { cn } from \"cn\"\n\nconst subscribeToLocation = () => () => undefined\n\nfunction useIsReauthenticationSignIn() {\n  return useSyncExternalStore(\n    subscribeToLocation,\n    () => isReauthenticationSignInURL(new URL(window.location.href)),\n    () => false\n  )\n}\n\nexport type ReauthenticationActionProps = {\n  className?: string\n  showTitle?: boolean\n}\n\nexport function ReauthenticationAction({\n  className,\n  showTitle = true\n}: ReauthenticationActionProps) {\n  const auth = useAuth()\n  const signOut = useSignOut(auth.authClient)\n\n  const handleReauthentication = () => {\n    const signInURL = getReauthenticationSignInURL(\n      new URL(window.location.href),\n      `${auth.basePaths.auth}/${auth.viewPaths.auth.signIn}`\n    )\n    signOut.mutate(undefined, {\n      onSuccess: () => auth.navigate({ to: signInURL })\n    })\n  }\n\n  return (\n    <div className={cn(\"flex flex-col items-start gap-3 p-4\", className)}>\n      <div className=\"flex flex-col gap-1\">\n        {showTitle ? (\n          <h3 className=\"text-sm font-medium\">\n            {auth.localization.settings.reauthenticationTitle}\n          </h3>\n        ) : null}\n        <p className=\"text-sm text-muted-foreground\">\n          {auth.localization.settings.reauthenticationDescription}\n        </p>\n      </div>\n      <Button\n        disabled={signOut.isPending}\n        onClick={handleReauthentication}\n        size=\"sm\"\n      >\n        {signOut.isPending ? <Spinner data-icon=\"inline-start\" /> : null}\n        {auth.localization.settings.reauthenticationAction}\n      </Button>\n    </div>\n  )\n}\n\nexport function ReauthenticationNotice() {\n  const auth = useAuth()\n  const isReauthenticationSignIn = useIsReauthenticationSignIn()\n  if (!isReauthenticationSignIn) return null\n\n  return (\n    <Alert className=\"mx-4 w-auto group-data-[size=sm]/card:mx-3\">\n      <AlertTitle>\n        {auth.localization.settings.reauthenticationTitle}\n      </AlertTitle>\n      <AlertDescription>\n        {auth.localization.settings.reauthenticationDescription}\n      </AlertDescription>\n    </Alert>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/reauthentication.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/settings/security/linked-account.tsx",
      "content": "\"use client\"\n\nimport {\n  type AuthSocialProvider,\n  getAuthErrorMessage,\n  getProviderId,\n  getProviderName,\n  isReauthenticationRequiredError\n} from \"@better-auth-ui/core\"\nimport {\n  renderProviderIcon,\n  useAccountInfo,\n  useAuth,\n  useLinkSocial,\n  useUnlinkAccount\n} from \"@better-auth-ui/react\"\nimport type { Account } from \"better-auth\"\nimport { Link2, Link2Off, Plug } from \"lucide-react\"\nimport { toast } from \"sonner\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogContent,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport {\n  Item,\n  ItemActions,\n  ItemContent,\n  ItemDescription,\n  ItemMedia,\n  ItemTitle\n} from \"@/components/ui/item\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { cn } from \"cn\"\nimport { ReauthenticationAction } from \"../../reauthentication\"\n\nexport type LinkedAccountProps = {\n  account?: Account\n  canUnlink?: boolean\n  provider: AuthSocialProvider | string\n}\n\n/**\n * Render a single linked social account row with provider info and link/unlink control.\n *\n * Fetches additional account information from the provider using the accountInfo API\n * and displays the provider name, account details, and a link/unlink button.\n *\n * @param account - The account object containing id, accountId, and providerId\n * @param provider - The provider id\n * @returns A JSX element containing the linked account row\n */\nexport function LinkedAccount({\n  account,\n  canUnlink = true,\n  provider\n}: LinkedAccountProps) {\n  const { authClient, baseURL, localization } = useAuth()\n\n  const { data: accountInfo, isPending: isLoadingInfo } = useAccountInfo(\n    authClient,\n    { query: { accountId: account?.id ?? \"\" } }\n  )\n\n  const { mutate: linkSocial, isPending: isLinking } = useLinkSocial(authClient)\n\n  const unlinkAccount = useUnlinkAccount(authClient, {\n    meta: { errorPresentation: \"inline\" },\n    onError: (error) => {\n      if (!isReauthenticationRequiredError(error)) {\n        const message = getAuthErrorMessage(error, localization)\n        if (message) {\n          console.error(\"[Better Auth UI]\", error)\n          toast.error(message)\n        }\n      }\n    },\n    onSuccess: () => toast.success(localization.settings.accountUnlinked)\n  })\n\n  const providerId = getProviderId(provider)\n  const providerIcon = renderProviderIcon(provider)\n  const providerName = getProviderName(provider)\n  const accountData = accountInfo?.data as\n    | { login?: string; username?: string }\n    | undefined\n\n  const displayName =\n    accountData?.login ||\n    accountData?.username ||\n    accountInfo?.user?.email ||\n    accountInfo?.user?.name ||\n    account?.accountId\n\n  const needsReauthentication = isReauthenticationRequiredError(\n    unlinkAccount.error\n  )\n\n  return (\n    <>\n      <Item>\n        <ItemMedia variant=\"icon\" className={cn(!account && \"opacity-50\")}>\n          {providerIcon ? providerIcon : <Plug />}\n        </ItemMedia>\n        <ItemContent>\n          <ItemTitle>{providerName}</ItemTitle>\n          {account && isLoadingInfo ? (\n            <Skeleton className=\"my-0.5 h-3 w-24\" />\n          ) : (\n            <ItemDescription>\n              {account\n                ? displayName\n                : localization.settings.linkProvider.replace(\n                    \"{{provider}}\",\n                    providerName\n                  )}\n            </ItemDescription>\n          )}\n        </ItemContent>\n        <ItemActions>\n          {account ? (\n            <Button\n              variant=\"outline\"\n              size=\"sm\"\n              onClick={() => unlinkAccount.mutate({ accountId: account.id })}\n              disabled={unlinkAccount.isPending || !canUnlink}\n              title={\n                canUnlink\n                  ? undefined\n                  : localization.settings.lastAccountUnlinkingDisabled\n              }\n              aria-label={localization.settings.unlinkProvider.replace(\n                \"{{provider}}\",\n                providerName\n              )}\n            >\n              {unlinkAccount.isPending ? <Spinner /> : <Link2Off />}\n              {localization.settings.unlinkProvider\n                .replace(\"{{provider}}\", \"\")\n                .trim()}\n            </Button>\n          ) : (\n            <Button\n              variant=\"outline\"\n              size=\"sm\"\n              onClick={() =>\n                linkSocial({\n                  provider: providerId,\n                  callbackURL: `${baseURL}${window.location.pathname}`\n                })\n              }\n              disabled={isLinking}\n              aria-label={localization.settings.linkProvider.replace(\n                \"{{provider}}\",\n                providerName\n              )}\n            >\n              {isLinking ? <Spinner /> : <Link2 />}\n              {localization.settings.link}\n            </Button>\n          )}\n        </ItemActions>\n      </Item>\n      {account && (\n        <Dialog\n          open={needsReauthentication}\n          onOpenChange={(nextOpen) => {\n            if (!nextOpen) unlinkAccount.reset()\n          }}\n        >\n          <DialogContent>\n            <DialogHeader>\n              <DialogTitle className=\"sr-only\">\n                {localization.settings.reauthenticationTitle}\n              </DialogTitle>\n            </DialogHeader>\n            <ReauthenticationAction showTitle={false} />\n          </DialogContent>\n        </Dialog>\n      )}\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/settings/security/linked-account.tsx"
    },
    {
      "path": "examples/start-shadcn-example/src/components/auth/settings/security/linked-accounts.tsx",
      "content": "\"use client\"\n\nimport { getProviderId } from \"@better-auth-ui/core\"\nimport { useAuth, useListAccounts } from \"@better-auth-ui/react\"\nimport { Fragment } from \"react\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport {\n  Item,\n  ItemContent,\n  ItemGroup,\n  ItemMedia,\n  ItemSeparator\n} from \"@/components/ui/item\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { cn } from \"cn\"\nimport { LinkedAccount } from \"./linked-account\"\n\nexport type LinkedAccountsProps = {\n  className?: string\n}\n\n/**\n * Render a card showing linked social accounts and available social providers to link.\n *\n * Linked accounts (excluding the \"credential\" provider) are shown with an unlink control;\n * available providers are shown with a link control. Button states and labels reflect\n * ongoing link/unlink activity and use localization for provider-specific text.\n *\n * @returns A JSX element containing the linked accounts card\n */\nexport function LinkedAccounts({ className }: LinkedAccountsProps) {\n  const {\n    authClient,\n    allowUnlinkingAllAccounts,\n    localization,\n    multipleAccountsPerProvider,\n    socialProviders\n  } = useAuth()\n\n  const { data: accounts, isPending } = useListAccounts(authClient)\n\n  const linkedAccounts = accounts?.filter(\n    (account) => account.providerId !== \"credential\"\n  )\n  const canUnlink =\n    allowUnlinkingAllAccounts === true || (accounts?.length ?? 0) > 1\n\n  const linkedProviderIds = new Set(linkedAccounts?.map((a) => a.providerId))\n\n  const availableProviders =\n    multipleAccountsPerProvider === false\n      ? socialProviders?.filter(\n          (provider) => !linkedProviderIds.has(getProviderId(provider))\n        )\n      : socialProviders\n\n  const allRows = [\n    ...(linkedAccounts?.map((account) => ({\n      key: account.id,\n      account,\n      provider:\n        socialProviders?.find(\n          (provider) => getProviderId(provider) === account.providerId\n        ) ?? account.providerId\n    })) ?? []),\n    ...(availableProviders?.map((provider) => ({\n      key: getProviderId(provider),\n      account: undefined,\n      provider\n    })) ?? [])\n  ]\n\n  return (\n    <div>\n      <h2 className=\"text-sm font-semibold mb-3\">\n        {localization.settings.linkedAccounts}\n      </h2>\n\n      <Card className={cn(\"p-0\", className)}>\n        <CardContent className=\"p-0\">\n          <ItemGroup className=\"gap-0\">\n            {isPending\n              ? socialProviders?.map((provider, index) => (\n                  <Fragment key={getProviderId(provider)}>\n                    {index > 0 && <ItemSeparator />}\n                    <AccountRowSkeleton />\n                  </Fragment>\n                ))\n              : allRows.map((row, index) => (\n                  <Fragment key={row.key}>\n                    {index > 0 && <ItemSeparator />}\n                    <LinkedAccount\n                      account={row.account}\n                      canUnlink={canUnlink}\n                      provider={row.provider}\n                    />\n                  </Fragment>\n                ))}\n          </ItemGroup>\n        </CardContent>\n      </Card>\n    </div>\n  )\n}\n\nfunction AccountRowSkeleton() {\n  return (\n    <Item>\n      <ItemMedia>\n        <Skeleton className=\"size-10 rounded-md\" />\n      </ItemMedia>\n      <ItemContent>\n        <Skeleton className=\"h-4 w-20\" />\n        <Skeleton className=\"h-3 w-32\" />\n      </ItemContent>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/settings/security/linked-accounts.tsx"
    }
  ],
  "type": "registry:component"
}