MyApp

Getting Started

Introduction

Content & Marketing

Add Blog PostAdd Blog AuthorAdd TestimonialsCustomize HeroAdd FAQ Items

Payments

Add Stripe ProductCreate Stripe SubscriptionAdd Stripe CouponCustomize CheckoutTest Payments Locally

Authentication

Customize Sign-InAdd OAuthImplement RolesProtect Routes

Content Management

Add Doc PageCreate Doc SectionCustomize Theme

Customization

Change ColorsAdd FontCustomize EmailsUse PostHog Analytics

Deployment

Deploy VercelDatabase Migrations

Advanced

Server ActionsAdd Rate Limiting
MyApp

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

  • Add OAuth Providers - Enable Google, GitHub, and more
  • Implement Roles - Add role-based access control
  • Protect Routes - Secure your application routes

Related Documentation

  • Authentication Overview - Learn about Clerk integration
  • Usage & Integration - Authentication patterns
  • Troubleshooting - Fix common issues

How is this guide ?

Last updated on

Test Payments Locally

Learn how to test Stripe payments and webhooks locally using Stripe CLI

Add OAuth

Learn how to add OAuth providers like Google, GitHub, Microsoft, and more

On this page

Goal
Prerequisites
Steps
Locate the Sign-In Form
Customize Form Styling
Customize Validation Rules
Customize Redirect Behavior
Add Remember Me Functionality
Preview Your Changes
Common Customizations
Change Form Layout
Add More OAuth Providers
Common Issues
Validation Not Working
Styling Not Applied
Redirect Not Working
OAuth Buttons Not Showing
Next Steps
Related Documentation