How to Use Pre-Built Validation Schemas (loginSchema & registerSchema) in refine-shadcn

Import loginSchema or registerSchema from @/lib/validation, pass them to zodResolver in your useForm hook, and receive fully-typed, validated form data for authentication flows without writing custom Zod logic.

The refine-shadcn repository provides production-ready authentication components backed by strict validation rules. These rules live as Zod schemas in packages/auth/src/lib/validation.ts, offering a single source of truth for login and registration validation that propagates TypeScript types throughout your application.

Where the Pre-Built Schemas Live

All validation logic is centralized in packages/auth/src/lib/validation.ts. This file exports two primary schemas and their inferred types:

  • loginSchema – Validates email format and password length (minimum 6 characters)
  • registerSchema – Validates name, email, password strength, password confirmation matching, and terms acceptance
  • LoginFormData – TypeScript type derived from loginSchema
  • RegisterFormData – TypeScript type derived from registerSchema

The repository exposes these through a path alias @/lib/validation, allowing you to import them cleanly from any component without relative path navigation.

Understanding the Schema Definitions

Login Validation Rules

The loginSchema enforces two fields with specific constraints. According to the source in packages/auth/src/lib/validation.ts, it requires a valid email address and a password of at least 6 characters:

// packages/auth/src/lib/validation.ts
export const loginSchema = z.object({
  email: z.string().email("Invalid email address").min(1, "Email is required"),
  password: z.string().min(6, "Password must be at least 6 characters").min(1, "Password is required"),
});

Registration Validation Rules

The registerSchema handles complex multi-field validation including password confirmation. It uses Zod's .refine() method to ensure confirmPassword matches password and that acceptTerms is true:

// packages/auth/src/lib/validation.ts
export const registerSchema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters"),
  email: z.string().email("Invalid email address"),
  password: z.string().min(8, "Password must be at least 8 characters"),
  confirmPassword: z.string(),
  acceptTerms: z.boolean(),
}).refine((data) => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ["confirmPassword"],
}).refine((data) => data.acceptTerms === true, {
  message: "You must accept the terms",
  path: ["acceptTerms"],
});

Integrating with react-hook-form

To activate validation in your components, wire the schemas into react-hook-form using @hookform/resolvers/zod. This bridges Zod's parsing logic with the form's state management.

Basic Login Form Setup

Import the schema and type, then pass them to useForm:

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { loginSchema, type LoginFormData } from "@/lib/validation";

const form = useForm<LoginFormData>({
  resolver: zodResolver(loginSchema),
  defaultValues: { email: "", password: "" },
});

Basic Registration Form Setup

The pattern is identical for registration, using the richer registerSchema:

import { registerSchema, type RegisterFormData } from "@/lib/validation";

const form = useForm<RegisterFormData>({
  resolver: zodResolver(registerSchema),
  defaultValues: {
    name: "",
    email: "",
    password: "",
    confirmPassword: "",
    acceptTerms: false,
  },
});

Complete Component Examples

Login Form Implementation

The packages/auth/src/components/login-form.tsx file demonstrates the canonical implementation. It combines the schema with shadcn/ui form primitives to render validation errors automatically:

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { loginSchema, type LoginFormData } from "@/lib/validation";
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@ferdiunal/refine-shadcn/ui/form";
import { Input } from "@ferdiunal/refine-shadcn/ui/input";
import { Button } from "@ferdiunal/refine-shadcn/ui/button";

export function LoginForm({ onSubmit }: { onSubmit: (data: LoginFormData) => void }) {
  const form = useForm<LoginFormData>({
    resolver: zodResolver(loginSchema),
    defaultValues: { email: "", password: "" },
  });

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input type="email" placeholder="you@example.com" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <FormField
          control={form.control}
          name="password"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Password</FormLabel>
              <FormControl>
                <Input type="password" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
        <Button type="submit" disabled={form.formState.isSubmitting}>
          Sign In
        </Button>
      </form>
    </Form>
  );
}

Registration Form Implementation

The registration form in packages/auth/src/components/register-form.tsx follows the same architecture but includes additional fields for name, confirm password, and terms acceptance. The FormMessage component automatically displays errors from the schema's .refine() calls, such as mismatched passwords or unchecked terms boxes.

Customizing the Schemas

Because validation is centralized in packages/auth/src/lib/validation.ts, you can modify authentication rules globally. For example, to require stronger passwords across your entire application:

  1. Open packages/auth/src/lib/validation.ts

  2. Update the password field in both schemas:

    password: z.string().min(12, "Password must be at least 12 characters").regex(/[A-Z]/, "Must contain uppercase"),
  3. Save the file—all forms using these schemas immediately enforce the new rules without component-level changes.

Summary

  • Centralized location: All pre-built validation schemas live in packages/auth/src/lib/validation.ts and export via @/lib/validation.
  • Two primary exports: loginSchema (email + 6-char password) and registerSchema (name, email, password match, terms).
  • Type inference: Import LoginFormData and RegisterFormData to type your form hooks and submit handlers.
  • react-hook-form integration: Use zodResolver(loginSchema) in useForm to activate validation.
  • Automatic UI feedback: Form primitives from @ferdiunal/refine-shadcn/ui consume the validation state and render errors via <FormMessage />.

Frequently Asked Questions

How do I access the validated data after form submission?

When form.handleSubmit executes, react-hook-form passes the validated data to your callback function with the exact shape defined by the schema. For example, if using registerSchema, your onSubmit handler receives a RegisterFormData object containing { name, email, password, confirmPassword, acceptTerms }, all guaranteed to meet the validation rules defined in packages/auth/src/lib/validation.ts.

Can I extend the pre-built schemas for additional fields?

Yes. Import the base schema and use Zod's .extend() method to add new fields, or compose a new schema that merges the existing one. Because the schemas are standard Zod objects, you can chain methods like .pick(), .omit(), or .merge() to create variations for different authentication flows while keeping the core validation logic intact.

What happens if I don't use the default UI components?

The schemas work with any react-hook-form implementation. Even without @ferdiunal/refine-shadcn/ui primitives, passing zodResolver(loginSchema) to useForm still provides type-safe validation state via form.formState.errors. You can display these errors manually by accessing form.formState.errors.email?.message or similar properties in your custom input components.

Do these schemas support internationalization?

The raw schemas in packages/auth/src/lib/validation.ts contain hardcoded English error messages. To support multiple languages, either override the error messages when instantiating the resolver or wrap the validation logic in a factory function that accepts translation helpers. The repository's own login and register components demonstrate integration with translation hooks for message customization.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →