We use tracking cookies to understand how you use the product and help us improve it. For more information on how we store cookies, read our  privacy policy.

Customize Sign-In

Learn how to customize the sign-in page styling, validation, and behavior

Learn how to customize Plainform's sign-in page to match your brand and add custom functionality.

Goal

By the end of this recipe, you'll have customized the sign-in page with your own styling, validation rules, and behavior.

Prerequisites

  • A working Plainform installation
  • Basic knowledge of React and TypeScript
  • Familiarity with Tailwind CSS

Plainform uses custom sign-in forms built with Clerk's useSignIn hook, not Clerk's pre-built components. This gives you full control over the UI.

Steps

Locate the Sign-In Form

The sign-in form is located at:

components/user/SignInForm.tsx

This component uses:

  • useSignIn hook from Clerk for authentication logic
  • react-hook-form for form state management
  • Zod for validation
  • Tailwind CSS for styling

Customize Form Styling

Update the form's appearance by modifying Tailwind classes:

components/user/SignInForm.tsx
export function SignInForm() {
  return (
    <div className="w-full max-w-md space-y-6">
      {/* Update card styling */}
      <Card className="border-2 border-brand shadow-xl">
        <CardHeader className="space-y-2 text-center">
          {/* Customize heading */}
          <CardTitle className="text-3xl font-bold text-brand">
            Welcome Back
          </CardTitle>
          <CardDescription className="text-base">
            Sign in to your account to continue
          </CardDescription>
        </CardHeader>
        
        <CardContent className="space-y-4">
          {/* Form fields */}
        </CardContent>
      </Card>
    </div>
  );
}

Common customizations:

  • Change card border and shadow
  • Update heading text and styling
  • Modify spacing with space-y-* classes
  • Add brand colors with text-brand or custom colors

Customize Validation Rules

Update the validation schema in validationSchemas/authSchemas.ts:

validationSchemas/authSchemas.ts
import { z } from 'zod';

export const signInSchema = z.object({
  identifier: z
    .string()
    .min(1, 'Email is required')
    .email('Please enter a valid email address'),
  password: z
    .string()
    .min(8, 'Password must be at least 8 characters')
    .max(100, 'Password is too long'),
});

Validation options:

  • Change minimum password length
  • Add custom error messages
  • Add regex patterns for email format
  • Add maximum length constraints

Keep validation rules consistent between sign-in and sign-up forms for better UX.

Customize Redirect Behavior

Change where users go after signing in:

components/user/SignInForm.tsx
if (signInAttempt.status === 'complete') {
  await setActive({ session: signInAttempt.createdSessionId });
  
  // Redirect based on user role or previous page
  const redirectUrl = searchParams.get('redirect') || '/dashboard';
  router.push(redirectUrl);
}

Redirect options:

  • Redirect to dashboard: router.push('/dashboard')
  • Redirect to previous page: Use redirect query parameter
  • Role-based redirect: Check user role and redirect accordingly

Add Remember Me Functionality

Add a "Remember Me" checkbox (optional):

components/user/SignInForm.tsx
const [rememberMe, setRememberMe] = useState(false);

// In the form
<div className="flex items-center space-x-2">
  <Checkbox
    id="remember"
    checked={rememberMe}
    onCheckedChange={(checked) => setRememberMe(checked as boolean)}
  />
  <label
    htmlFor="remember"
    className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
  >
    Remember me for 30 days
  </label>
</div>

// When creating session
if (signInAttempt.status === 'complete') {
  await setActive({
    session: signInAttempt.createdSessionId,
    // Extend session if remember me is checked
    beforeEmit: rememberMe ? undefined : () => {},
  });
}

Clerk handles session persistence automatically. The "Remember Me" option can extend the session duration.

Preview Your Changes

Start your development server:

npm run dev

Navigate to http://localhost:3000/sign-in to see your customized sign-in page.

Common Customizations

Change Form Layout

Switch to a horizontal layout for wider screens:

components/user/SignInForm.tsx
<div className="grid md:grid-cols-2 gap-6">
  <div className="space-y-4">
    {/* Left side: Form */}
    <SignInForm />
  </div>
  <div className="hidden md:flex items-center justify-center bg-muted rounded-lg">
    {/* Right side: Image or branding */}
    <img src="/signin-illustration.svg" alt="Sign in" />
  </div>
</div>

Add More OAuth Providers

To add additional OAuth providers beyond Google and GitHub:

components/user/SignInForm.tsx
<div className="grid grid-cols-2 gap-4">
  <OAuthConnection strategy="oauth_google" icon="Google">
    Google
  </OAuthConnection>
  <OAuthConnection strategy="oauth_github" icon="Github">
    GitHub
  </OAuthConnection>
  <OAuthConnection strategy="oauth_microsoft" icon="Microsoft">
    Microsoft
  </OAuthConnection>
  <OAuthConnection strategy="oauth_apple" icon="Apple">
    Apple
  </OAuthConnection>
</div>

Available OAuth providers:

  • oauth_google - Google
  • oauth_github - GitHub
  • oauth_microsoft - Microsoft
  • oauth_apple - Apple
  • oauth_facebook - Facebook
  • oauth_linkedin - LinkedIn
  • oauth_twitter - Twitter

Enable each provider in Clerk Dashboard → Social Connections before adding the button.

Common Issues

Validation Not Working

  • Verify the schema is imported correctly in SignInForm.tsx
  • Check that react-hook-form resolver is configured with Zod
  • Ensure form fields have correct name attributes matching schema

Styling Not Applied

  • Clear browser cache and restart dev server
  • Check Tailwind CSS classes are valid
  • Verify globals.css is imported in root layout
  • Run npm run dev to rebuild Tailwind

Redirect Not Working

  • Verify the redirect URL is valid and accessible
  • Check middleware configuration in proxy.ts
  • Ensure the target route exists in your app

OAuth Buttons Not Showing

  • Verify OAuth providers are enabled in Clerk Dashboard
  • Check OAuthConnection component is imported correctly
  • Ensure Clerk environment variables are set

Next Steps

How is this guide ?

Last updated on