Signup form component with email, password, and display name fields, including comprehensive validation.
Location: src/components/forms/SignupForm.tsx
Overview
The SignupForm component provides a complete user registration experience with email, password, and display name fields. It includes robust client-side validation (including password strength requirements), error handling, loading states, and automatic redirection to the dashboard on success. The form creates the user with the Firebase client SDK, then exchanges the ID token via /api/auth/login. It does not POST credentials to a server signup route.
Quick Customisation
This component is highly customizable:
- Success callback - Custom
onSuccesshandler for post-signup actions - Error callback - Custom
onErrorhandler for error handling - Redirect destination - Defaults to
/dashboard(configure inconfig.ts) - Validation rules - Password strength, email format, required fields
- Styling - Uses DaisyUI components (Input, Button, Alert)
- Helper text - Password field includes helper text for requirements
Usage
import SignupForm from "@/components/forms/SignupForm";
<SignupForm />
Props
| Prop | Type | Default | Description |
|---|---|---|---|
onSuccess | () => void | undefined | Callback when signup succeeds (overrides default redirect) |
onError | (error: string) => void | undefined | Callback when signup fails |
Examples
Basic Usage
import SignupForm from "@/components/forms/SignupForm";
// Standard signup form
<SignupForm />
With Custom Success Handler
import SignupForm from "@/components/forms/SignupForm";
import { useRouter } from "next/navigation";
function CustomSignup() {
const router = useRouter();
return (
<SignupForm
onSuccess={() => {
// Custom logic after signup
console.log("User registered!");
router.push("/onboarding");
}}
/>
);
}
With Custom Error Handling
import SignupForm from "@/components/forms/SignupForm";
<SignupForm
onError={(error) => {
// Custom error handling
console.error("Signup failed:", error);
// Show custom notification, etc.
}}
/>
Complete Example with Both Callbacks
import SignupForm from "@/components/forms/SignupForm";
<SignupForm
onSuccess={() => {
// Redirect to onboarding
router.push("/onboarding");
}}
onError={(error) => {
// Handle error (e.g., show toast notification)
toast.error(error);
}}
/>
Features
Form Fields
- Display Name input - User's display name, required field
- Email input - Validated email format, required field
- Password input - Required field with strength validation, masked input
- Helper text - Password field shows requirements
Validation
- Display Name - Required field
- Email format - Validates email pattern (
/\S+@\S+\.\S+/) - Password strength:
- Minimum 8 characters
- At least one letter
- At least one number
- Required fields - All fields are required
- Error display - Shows field-specific and general errors
User Experience
- Loading state - Shows loading spinner on submit button
- Disabled inputs - Inputs disabled during submission
- Auto-redirect - Redirects to
/dashboardon success (or customonSuccess) - Error messages - Clear error messages for validation and API errors
- Helper text - Password field shows requirements below input
Component Structure
interface SignupFormProps {
onSuccess?: () => void;
onError?: (error: string) => void;
}
const SignupForm = ({ onSuccess, onError }: SignupFormProps) => {
// State management
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [displayName, setDisplayName] = useState("");
const [loading, setLoading] = useState(false);
const [errors, setErrors] = useState({});
// Validation and submit logic
// ...
};
Validation Rules
Display Name
- Required: Yes
- Error message: "Display name is required"
- Required: Yes
- Format: Must be valid email format (
user@example.com) - Error message: "Email is required" or "Email is invalid"
Password
- Required: Yes
- Minimum length: 8 characters
- Must contain: At least one letter (a-z, A-Z)
- Must contain: At least one number (0-9)
- Error messages:
- "Password is required"
- "Password must be at least 8 characters"
- "Password must contain at least one letter"
- "Password must contain at least one number"
API Integration
Flow (same session path as login — do not POST credentials to /api/auth/signup from this form):
const credential = await createUserWithEmailAndPassword(auth, email, password);
await updateProfile(credential.user, { displayName });
const idToken = await credential.user.getIdToken(true); // refresh so displayName is in claims
// POST /api/auth/login
{ idToken: string }
Response:
- Success: Bootstraps
users/{uid}if needed, sets httpOnly Firebasesessioncookie, returns{ success: true, data: ... } - Error: Firebase client errors (e.g. email already in use) or API error message
Customisation
Change Redirect Destination
Option 1: Use onSuccess prop:
<SignupForm
onSuccess={() => router.push("/onboarding")}
/>
Option 2: Update config.ts:
// config.ts
auth: {
callbackUrl: "/your-custom-page",
// ...
}
Customise Password Requirements
Edit the validate function in SignupForm.tsx:
const validate = () => {
const newErrors = {};
// Custom password requirements
if (password.length < 12) { // Change minimum length
newErrors.password = "Password must be at least 12 characters";
}
if (!/[A-Z]/.test(password)) { // Require uppercase
newErrors.password = "Password must contain an uppercase letter";
}
if (!/[!@#$%^&*]/.test(password)) { // Require special character
newErrors.password = "Password must contain a special character";
}
// ... existing validation
return Object.keys(newErrors).length === 0;
};
Update helper text:
<Input
type="password"
label="Password"
helperText="Must be at least 12 characters with uppercase, lowercase, number, and special character"
// ...
/>
Customise Validation
Edit the validate function in SignupForm.tsx:
const validate = () => {
const newErrors = {};
// Custom display name validation
if (displayName.length < 3) {
newErrors.displayName = "Display name must be at least 3 characters";
}
// Custom email validation
if (email && !email.endsWith("@yourcompany.com")) {
newErrors.email = "Must use company email";
}
// ... existing validation
return Object.keys(newErrors).length === 0;
};
Customise Styling
The form uses DaisyUI components. Modify classes:
// In SignupForm.tsx
<form onSubmit={handleSubmit} className="space-y-6"> {/* Change spacing */}
{/* ... */}
</form>
Styling Notes
- Form container: Uses
space-y-4for vertical spacing - Inputs: Uses
Inputcomponent with validation states - Button: Uses
Buttoncomponent with loading state - Errors: Uses DaisyUI
alert alert-errorfor error display - Helper text: Password field shows requirements below input
Best Practices
- Password strength - Current requirements are a good balance (not too strict, not too weak)
- Error handling - Always provide
onErrorcallback for better UX - Loading states - Form automatically handles loading (inputs disabled)
- Accessibility - Form includes proper labels and autoComplete attributes
- Security - Password field uses
type="password"for masking - User feedback - Helper text helps users understand requirements upfront
Tips
- Success callback: Overrides default redirect if provided
- Error callback: Use for custom error notifications (toasts, alerts)
- Password requirements: Helper text shows requirements to reduce errors
- Auto-complete: Form includes proper
autoCompleteattributes - Validation: Client-side validation happens before API call
- Display name: Use for user's display name (can be different from email)
Learn More
- Input Component - Base input component used in form
- Button Component - Submit button component
- LoginForm Component - User login form
- Authentication Features - Complete auth guide
- Validation Features - Validation patterns and best practices