UI Components
Complete @keyloom/ui component library reference - authentication forms, organization management, primitives, theming, and customization.
@keyloom/ui
Keyloom's comprehensive UI component library for authentication flows, organization management, and reusable primitives. Built with Tailwind CSS and Radix UI.
Prerequisites
- React 18+ or Next.js 13+
- Tailwind CSS 3.4+
- Keyloom handler configured at
/api/auth/[...keyloom]
Installation
pnpm add @keyloom/ui clsx lucide-react tailwindcss @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-checkbox @radix-ui/react-avatarSetup
1. Tailwind configuration
const keyloom = require("@keyloom/ui/theme/tailwind-preset.cjs");
module.exports = {
presets: [keyloom],
content: [
"./app/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./node_modules/@keyloom/ui/dist/**/*.{js,ts,jsx,tsx}",
],
};2. CSS variables
@import "@keyloom/ui/theme/css-vars.css";
@tailwind base;
@tailwind components;
@tailwind utilities;3. AuthUIProvider (optional)
import { AuthUIProvider } from "@keyloom/ui";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
<AuthUIProvider>{children}</AuthUIProvider>
</body>
</html>
);
}Authentication Components
SignInForm
Email/password sign-in form with validation and error handling.
import { SignInForm } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
export default function SignInPage() {
return (
<div className="min-h-screen grid place-items-center p-6">
<Card className="w-full max-w-md p-6">
<h1 className="text-2xl font-semibold mb-6">Welcome back</h1>
<SignInForm
redirectTo="/dashboard"
endpoint="/api/auth/login"
onSuccess={(res) => console.log("Signed in:", res)}
/>
</Card>
</div>
);
}Props:
redirectTo?: string- Redirect URL after successful sign-in (default: "/")endpoint?: string- API endpoint for sign-in (default: "/api/auth/login")onSuccess?: (res: any) => void- Success callback
MagicLinkForm
Passwordless authentication form that sends magic links via email.
import { MagicLinkForm } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
export default function MagicLinkPage() {
return (
<div className="min-h-screen grid place-items-center p-6">
<Card className="w-full max-w-md p-6">
<h1 className="text-2xl font-semibold mb-6">Sign in with Magic Link</h1>
<MagicLinkForm
onSuccess={() => console.log("Magic link sent!")}
onError={(error) => console.error("Error:", error)}
/>
</Card>
</div>
);
}Props:
onSuccess?: () => void- Success callback when magic link is sentonError?: (error: string) => void- Error callbackclassName?: string- Additional CSS classes
SignUpForm
User registration form with email/password and validation.
import { SignUpForm } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
export default function SignUpPage() {
return (
<div className="min-h-screen grid place-items-center p-6">
<Card className="w-full max-w-md p-6">
<h1 className="text-2xl font-semibold mb-6">Create account</h1>
<SignUpForm redirectTo="/dashboard" endpoint="/api/auth/register" />
</Card>
</div>
);
}Props:
redirectTo?: string- Redirect URL after successful sign-upendpoint?: string- API endpoint for registrationonSuccess?: (res: any) => void- Success callback
Providers
OAuth provider buttons with automatic provider detection.
import { Providers } from "@keyloom/ui/auth";
export function AuthProviders() {
return (
<Providers
callbackUrl="/dashboard"
providers={[
{ id: "github", name: "GitHub" },
{ id: "google", name: "Google" },
{ id: "microsoft", name: "Microsoft" },
]}
/>
);
}Props:
callbackUrl?: string- Redirect URL after OAuth callbackproviders?: Provider[]- Array of provider configurations
UserButton
User profile dropdown with sign-out functionality.
import { UserButton } from "@keyloom/ui/auth";
export function NavBar() {
return (
<nav className="flex justify-between items-center p-4">
<h1>My App</h1>
<UserButton />
</nav>
);
}ForgotPasswordForm
Password reset request form for users who forgot their password.
import { ForgotPasswordForm } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
export default function ForgotPasswordPage() {
return (
<div className="min-h-screen grid place-items-center p-6">
<Card className="w-full max-w-md p-6">
<h1 className="text-2xl font-semibold mb-6">Reset your password</h1>
<ForgotPasswordForm endpoint="/api/auth/forgot-password" />
</Card>
</div>
);
}ResetPasswordForm
Password reset form for users with a valid reset token.
import { ResetPasswordForm } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
export default function ResetPasswordPage() {
return (
<div className="min-h-screen grid place-items-center p-6">
<Card className="w-full max-w-md p-6">
<h1 className="text-2xl font-semibold mb-6">Set new password</h1>
<ResetPasswordForm endpoint="/api/auth/reset-password" />
</Card>
</div>
);
}EmailVerificationForm
Email verification form for confirming user email addresses.
import { EmailVerificationForm } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
export default function VerifyEmailPage() {
return (
<div className="min-h-screen grid place-items-center p-6">
<Card className="w-full max-w-md p-6">
<h1 className="text-2xl font-semibold mb-6">Verify your email</h1>
<EmailVerificationForm endpoint="/api/auth/verify-email" />
</Card>
</div>
);
}AuthLayout Components
Pre-built layout components for authentication pages with consistent styling.
import { AuthLayout } from "@keyloom/ui/auth";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<AuthLayout
title="Welcome to My App"
subtitle="Secure authentication platform"
>
{children}
</AuthLayout>
);
}Available Layout Components:
AuthLayout- General authentication layoutSignInLayout- Optimized for sign-in pagesSignUpLayout- Optimized for registration pagesForgotPasswordLayout- For password reset flowsResetPasswordLayout- For password reset formsVerifyEmailLayout- For email verification
RedirectToSignIn & Authentication Guards
Components and utilities for protecting routes and redirecting unauthenticated users.
import { RedirectToSignIn, withAuth } from "@keyloom/ui/auth";
import { useSession } from "@keyloom/react";
function ProtectedContent() {
return <div>This content is protected</div>;
}
// Option 1: Using RedirectToSignIn component
export function ProtectedPage() {
const { data: session, status } = useSession();
if (status === "loading") return <div>Loading...</div>;
if (!session) return <RedirectToSignIn />;
return <ProtectedContent />;
}
// Option 2: Using withAuth HOC
export default withAuth(ProtectedContent);
// Option 3: Using useRequireAuth hook
import { useRequireAuth } from "@keyloom/ui/auth";
export function AnotherProtectedPage() {
useRequireAuth(); // Automatically redirects if not authenticated
return <ProtectedContent />;
}Organization Components
OrganizationSwitcher
Organization switcher dropdown for multi-tenant applications.
import { OrganizationSwitcher } from "@keyloom/ui/auth";
export function AppHeader() {
return (
<header className="flex justify-between items-center p-4">
<h1>My App</h1>
<div className="flex items-center gap-4">
<OrganizationSwitcher />
<UserButton />
</div>
</header>
);
}MembersTable
Data table for organization member management with roles and actions.
import { MembersTable } from "@keyloom/ui/org";
export default function MembersPage() {
return (
<div className="p-6">
<h1 className="text-2xl font-semibold mb-6">Team Members</h1>
<MembersTable
orgId="org_123"
onInvite={() => console.log("Invite member")}
onRemove={(memberId) => console.log("Remove:", memberId)}
/>
</div>
);
}CreateOrgDialog
Modal dialog for creating new organizations.
import { CreateOrgDialog } from "@keyloom/ui/org";
import { Button } from "@keyloom/ui/components/button";
export function CreateOrgButton() {
return (
<CreateOrgDialog
trigger={<Button>Create Organization</Button>}
onSuccess={(org) => console.log("Created:", org)}
/>
);
}InviteMemberDialog
Modal dialog for inviting new team members.
import { InviteMemberDialog } from "@keyloom/ui/org";
import { Button } from "@keyloom/ui/components/button";
export function InviteMemberButton() {
return (
<InviteMemberDialog
trigger={<Button>Invite Member</Button>}
orgId="org_123"
onSuccess={(invitation) => console.log("Invited:", invitation)}
/>
);
}OrganizationView
Complete organization management interface with settings and member overview.
import { OrganizationView } from "@keyloom/ui/auth";
export default function OrganizationPage({ params }: { params: { id: string } }) {
return (
<OrganizationView
organizationId={params.id}
onUpdateSettings={(settings) => console.log("Updated:", settings)}
onManageMembers={() => console.log("Manage members")}
/>
);
}OrganizationMemberCard
Individual member card component for displaying member information and actions.
import { OrganizationMemberCard } from "@keyloom/ui/auth";
export function MemberCard({ member }: { member: OrganizationMember }) {
return (
<OrganizationMemberCard
member={member}
onUpdateRole={(role) => console.log("Update role:", role)}
onRemove={() => console.log("Remove member")}
showActions={true}
/>
);
}Account Management Components
AccountView
Complete account management interface with profile editing and settings.
import { AccountView } from "@keyloom/ui/auth";
export default function AccountPage() {
return (
<AccountView
onUpdateProfile={(profile) => console.log("Updated:", profile)}
onDeleteAccount={() => console.log("Delete account")}
showDangerZone={true}
/>
);
}AccountSettingsCard
Account settings management card with form controls.
import { AccountSettingsCard } from "@keyloom/ui/auth";
export function AccountSettings() {
return (
<AccountSettingsCard
onUpdateEmail={(email) => console.log("Update email:", email)}
onUpdatePassword={(password) => console.log("Update password")}
onUpdateProfile={(profile) => console.log("Update profile:", profile)}
/>
);
}SecuritySettingsCard
Security settings management including password, 2FA, and session management.
import { SecuritySettingsCard } from "@keyloom/ui/auth";
export function SecuritySettings() {
return (
<SecuritySettingsCard
onChangePassword={() => console.log("Change password")}
onEnable2FA={() => console.log("Enable 2FA")}
onManageSessions={() => console.log("Manage sessions")}
/>
);
}ApiKeysCard
API key management interface for developers and integrations.
import { ApiKeysCard } from "@keyloom/ui/auth";
export function ApiKeys() {
return (
<ApiKeysCard
onCreateKey={(name) => console.log("Create key:", name)}
onRevokeKey={(keyId) => console.log("Revoke key:", keyId)}
onRegenerateKey={(keyId) => console.log("Regenerate key:", keyId)}
/>
);
}User Profile Components
UserProfile
Complete user profile display with avatar, information, and actions.
import { UserProfile } from "@keyloom/ui/auth";
export function ProfilePage() {
return (
<UserProfile
user={user}
onEdit={() => console.log("Edit profile")}
onChangeAvatar={(file) => console.log("Change avatar:", file)}
showEditButton={true}
/>
);
}UserProfileCompact
Compact user profile variant for sidebars and small spaces.
import { UserProfileCompact } from "@keyloom/ui/auth";
export function Sidebar() {
return (
<div className="w-64 bg-background border-r">
<UserProfileCompact
user={user}
showStatus={true}
onClick={() => console.log("Profile clicked")}
/>
</div>
);
}RBAC Components
RoleGate
Conditional rendering based on user roles and permissions.
import { RoleGate } from "@keyloom/ui/rbac";
export function AdminPanel() {
return (
<div>
<h2>Dashboard</h2>
<RoleGate roles={["admin", "owner"]}>
<div className="bg-red-50 p-4 rounded">
<h3>Admin Only Content</h3>
<p>This is only visible to admins and owners.</p>
</div>
</RoleGate>
<RoleGate permissions={["users:write"]}>
<button>Manage Users</button>
</RoleGate>
</div>
);
}Props:
roles?: string[]- Required roles for accesspermissions?: string[]- Required permissions for accessfallback?: React.ReactNode- Content to show when access denied
Primitive Components
Button
Styled button component with variants and sizes.
import { Button } from "@keyloom/ui/components/button";
export function ActionButtons() {
return (
<div className="flex gap-2">
<Button variant="primary" size="md">
Primary Action
</Button>
<Button variant="outline" size="md">
Secondary
</Button>
<Button variant="danger" size="sm">
Delete
</Button>
</div>
);
}Props:
variant?: 'default' | 'primary' | 'ghost' | 'outline' | 'danger'size?: 'sm' | 'md' | 'lg'
Card
Container component with consistent styling.
import { Card } from "@keyloom/ui/components/card";
export function StatsCard({ title, value }: { title: string; value: string }) {
return (
<Card className="p-6">
<h3 className="text-sm font-medium text-muted-foreground">{title}</h3>
<p className="text-2xl font-bold">{value}</p>
</Card>
);
}Input
Form input component with validation states.
import { Input } from "@keyloom/ui/components/input";
import { Label } from "@keyloom/ui/components/label";
export function ContactForm() {
return (
<form className="space-y-4">
<div>
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="Enter your email"
required
/>
</div>
<div>
<Label htmlFor="message">Message</Label>
<Input id="message" as="textarea" placeholder="Your message" rows={4} />
</div>
</form>
);
}Dialog
Modal dialog component built on Radix UI.
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@keyloom/ui/components/dialog";
import { Button } from "@keyloom/ui/components/button";
export function ConfirmDialog({ onConfirm }: { onConfirm: () => void }) {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="danger">Delete Account</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you sure?</DialogTitle>
</DialogHeader>
<p>This action cannot be undone.</p>
<div className="flex justify-end gap-2 mt-4">
<Button variant="outline">Cancel</Button>
<Button variant="danger" onClick={onConfirm}>
Delete
</Button>
</div>
</DialogContent>
</Dialog>
);
}Theming and Customization
Design Tokens
Access design tokens programmatically:
import { theme } from "@keyloom/ui";
export function CustomComponent() {
return (
<div
style={{
backgroundColor: theme.colors.primary[500],
borderRadius: theme.radii.md,
padding: theme.spacing[4],
}}
>
Custom styled component
</div>
);
}CSS Variables
Use CSS variables for dynamic theming:
.custom-button {
background-color: var(--kl-primary);
color: var(--kl-primary-fg);
border-radius: var(--kl-radius);
box-shadow: var(--kl-shadow-sm);
}
.dark .custom-button {
/* Dark mode automatically handled by CSS variables */
}Tailwind Classes
Use Keyloom's Tailwind preset classes:
export function ThemedCard() {
return (
<div className="bg-background text-foreground border border-border rounded-lg p-6 shadow-sm">
<h3 className="text-primary font-semibold">Themed Card</h3>
<p className="text-muted-foreground">Using Keyloom design tokens</p>
</div>
);
}Dark Mode
Dark mode is handled automatically via CSS variables:
"use client";
import { useEffect, useState } from "react";
import { Button } from "@keyloom/ui/components/button";
export function ThemeToggle() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
const isDarkMode = document.documentElement.classList.contains("dark");
setIsDark(isDarkMode);
}, []);
const toggleTheme = () => {
document.documentElement.classList.toggle("dark");
setIsDark(!isDark);
};
return (
<Button variant="ghost" onClick={toggleTheme}>
{isDark ? "☀️" : "🌙"}
</Button>
);
}Advanced Usage
Complete Authentication Flow
import { SignInForm, SignUpForm, Providers } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
import { Button } from "@keyloom/ui/components/button";
import { useState } from "react";
export default function AuthPage() {
const [mode, setMode] = useState<"signin" | "signup">("signin");
return (
<div className="min-h-screen grid place-items-center p-6">
<Card className="w-full max-w-md p-6 space-y-6">
<div className="text-center">
<h1 className="text-2xl font-semibold">
{mode === "signin" ? "Welcome back" : "Create account"}
</h1>
</div>
<Providers callbackUrl="/dashboard" />
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t" />
</div>
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-background px-2 text-muted-foreground">
Or continue with
</span>
</div>
</div>
{mode === "signin" ? (
<SignInForm redirectTo="/dashboard" />
) : (
<SignUpForm redirectTo="/dashboard" />
)}
<div className="text-center">
<Button
variant="ghost"
onClick={() => setMode(mode === "signin" ? "signup" : "signin")}
>
{mode === "signin"
? "Don't have an account? Sign up"
: "Already have an account? Sign in"}
</Button>
</div>
</Card>
</div>
);
}Organization Dashboard
import { OrganizationSwitcher, MembersTable, CreateOrgDialog } from "@keyloom/ui/auth";
import { RoleGate } from "@keyloom/ui/rbac";
import { UserButton } from "@keyloom/ui/auth";
import { Card } from "@keyloom/ui/components/card";
import { Button } from "@keyloom/ui/components/button";
export default function OrgDashboard() {
return (
<div className="min-h-screen bg-muted/50">
<header className="bg-background border-b px-6 py-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-4">
<h1 className="text-xl font-semibold">Dashboard</h1>
<OrganizationSwitcher />
</div>
<div className="flex items-center gap-2">
<RoleGate roles={["owner", "admin"]}>
<CreateOrgDialog
trigger={<Button size="sm">New Organization</Button>}
/>
</RoleGate>
<UserButton />
</div>
</div>
</header>
<main className="p-6">
<div className="grid gap-6">
<Card className="p-6">
<h2 className="text-lg font-semibold mb-4">Team Members</h2>
<MembersTable orgId="current-org" />
</Card>
</div>
</main>
</div>
);
}Performance Considerations
- Tree Shaking: Import only the components you need
- Bundle Size: Core components are lightweight (~15KB gzipped)
- SSR Compatible: All components work with Next.js SSR/SSG
- Lazy Loading: Use dynamic imports for large components
Accessibility
- ARIA Labels: All interactive components include proper ARIA attributes
- Keyboard Navigation: Full keyboard support for all components
- Screen Readers: Semantic HTML and proper labeling
- Focus Management: Logical focus order and visible focus indicators
Troubleshooting
Styles not applying
- Ensure Tailwind preset is configured correctly
- Import CSS variables in your global CSS file
- Check content paths include
@keyloom/uidist files
Components not rendering
- Verify all peer dependencies are installed
- Check React version compatibility (18+)
- Ensure proper import paths
TypeScript errors
- Update to latest TypeScript version (5.0+)
- Check component prop types match expected interface
- Verify proper generic type usage
See also
- UI Patterns: /docs/getting-started/ui-patterns
- React Hooks: /docs/react/hooks
- Installation: /docs/getting-started/installation
- RBAC: /docs/core/rbac
Next steps
- Set up Tailwind configuration with Keyloom preset
- Import CSS variables and configure theming
- Build your authentication flow with provided components
- Customize components to match your brand
How is this guide?