Prompt Details
Model
(claude-4-6-sonnet)
Token size
550
Example input
[USER_SPEC]: Registration form in English (professional tone). Fields: first name, last name, email, password, confirm password, date of birth. Validation: email must be a business address (no gmail/yahoo/hotmail), password min 8 chars with uppercase + number + special char, passwords must match, age 18+. No masking needed
Example output
import { useState } from "react";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
// ─── Zod Schema ───────────────────────────────────────────────────────────────
const BLOCKED_DOMAINS = ["gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "live.com", "icloud.com", "aol.com", "mail.com", "protonmail.com", "yandex.com"];
const registrationSchema = z
.object({
firstName: z
.string()
.min(1, "First name is required.")
.min(2, "First name must be at least 2 characters.")
.max(50, "First name must not exceed 50 characters."),
lastName: z
.string()
.min(1, "Last name is required.")
.min(2, "Last name must be at least 2 characters.")
.max(50, "Last name must not exceed 50 characters."),
email: z
.string()
.min(1, "Email address is required.")
.email("Please enter a valid email address.")
.refine((val) => {
const domain = val.split("@")[1]?.toLowerCase();
if (!domain) return false;
return !BLOCKED_DOMAINS.includes(domain);
}, "Please use a business email address. Personal email providers (Gmail, Yahoo, Hotmail, etc.) are not accepted."),
password: z
.string()
.min(1, "Password is required.")
.min(8, "Password must be at least 8 characters.")
.refine((val) => /[A-Z]/.test(val), "Password must contain at least one uppercase letter.")
.refine((val) => /[0-9]/.test(val), "Password must contain at least one number.")
.refine((val) => /[^A-Za-z0-9]/.test(val), "Password must contain at least one special character."),
confirmPassword: z.string().min(1, "Please confirm your password."),
dateOfBirth: z
.string()
.min(1, "Date of birth is required.")
.refine((val) => {
const date = new Date(val);
return !isNaN(date.getTime());
}, "Please enter a valid date.")
.refine((val) => {
const dob = new Date(val);
const today = new Date();
const age = today.getFullYear() - dob.getFullYear();
const m = today.getMonth() - dob.getMonth();
const adjustedAge = m < 0 || (m === 0 && today.getDate() < dob.getDate()) ? age - 1 : age;
return adjustedAge >= 18;
}, "You must be at least 18 years old to register."),
})
.refine((data) => data.password === data.confirmPassword, {
path: ["confirmPassword"],
message: "Passwords do not match.",
});
// ─── Inferred Types ───────────────────────────────────────────────────────────
type RegistrationFormData = z.infer<typeof registrationSchema>;
// ─── Helper Functions ─────────────────────────────────────────────────────────
function getMaxDateOfBirth(): string {
const today = new Date();
today.setFullYear(today.getFullYear() - 18);
return today.toISOString().split("T")[0];
}
function getMinDateOfBirth(): string {
const today = new Date();
today.setFullYear(today.getFullYear() - 120);
return today.toISOString().split("T")[0];
}
function getPasswordStrength(password: string): { level: number; label: string; color: string } {
if (!password) return { level: 0, label: "", color: "" };
let score = 0;
if (password.length >= 8) score++;
if (password.length >= 12) score++;
if (/[A-Z]/.test(password)) score++;
if (/[0-9]/.test(password)) score++;
if (/[^A-Za-z0-9]/.test(password)) score++;
if (score <= 2) return { level: 1, label: "Weak", color: "bg-red-500" };
if (score <= 3) return { level: 2, label: "Fair", color: "bg-amber-500" };
if (score <= 4) return { level: 3, label: "Good", color: "bg-blue-500" };
return { level: 4, label: "Strong", color: "bg-emerald-500" };
}
// ─── Component ────────────────────────────────────────────────────────────────
export default function RegistrationForm() {
const [submitted, setSubmitted] = useState(false);
const [submittedData, setSubmittedData] = useState<RegistrationFormData | null>(null);
const {
register,
handleSubmit,
control,
watch,
formState: { errors, isSubmitting },
} = useForm<RegistrationFormData>({
resolver: zodResolver(registrationSchema),
mode: "onBlur",
});
const passwordValue = watch("password", "");
const strength = getPasswordStrength(passwordValue);
const onSubmit = (data: RegistrationFormData) => {
console.log("Registration data:", data);
setSubmittedData(data);
setSubmitted(true);
};
if (submitted && submittedData) {
return (
<div className="min-h-screen bg-slate-950 flex items-center justify-center p-6">
<div className="w-full max-w-md">
<div className="bg-slate-900 border border-slate-700/50 rounded-2xl p-8 shadow-2xl shadow-black/40 text-center">
<div className="w-16 h-16 bg-emerald-500/10 border border-emerald-500/30 rounded-full flex items-center justify-center mx-auto mb-6">
<svg className="w-8 h-8 text-emerald-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
</svg>
</div>
<h2 className="text-2xl font-semibold text-white mb-2 tracking-tight">Registration Successful</h2>
<p className="text-slate-400 text-sm leading-relaxed">
Welcome, <span className="text-white font-medium">{submittedData.firstName} {submittedData.lastName}</span>. Your account has been created successfully. A confirmation email will be sent to <span className="text-white font-medium">{submittedData.email}</span>.
</p>
<button
onClick={() => { setSubmitted(false); setSubmittedData(null); }}
className="mt-6 text-sm text-slate-500 hover:text-slate-300 transition-colors duration-200 underline underline-offset-4"
>
Register another account
</button>
</div>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-slate-950 flex items-center justify-center p-6">
{/* Background grid */}
<div className="fixed inset-0 bg-[linear-gradient(to_right,#1e293b_1px,transparent_1px),linear-gradient(to_bottom,#1e293b_1px,transparent_1px)] bg-[size:64px_64px] opacity-40 pointer-events-none" />
<div className="fixed inset-0 bg-[radial-gradient(ellipse_80%_50%_at_50%_-20%,rgba(99,102,241,0.15),transparent)] pointer-events-none" />
<div className="relative w-full max-w-lg">
{/* Header */}
<div className="mb-8 text-center">
<div className="inline-flex items-center gap-2 bg-indigo-500/10 border border-indigo-500/20 rounded-full px-4 py-1.5 mb-5">
<div className="w-1.5 h-1.5 rounded-full bg-indigo-400 animate-pulse" />
<span className="text-xs text-indigo-300 font-medium tracking-wide uppercase">New Account</span>
</div>
<h1 className="text-3xl font-bold text-white tracking-tight mb-2">Create your account</h1>
<p className="text-slate-400 text-sm">Complete the form below to get started with our platform.</p>
</div>
{/* Form card */}
<div className="bg-slate-900/80 backdrop-blur-sm border border-slate-700/50 rounded-2xl shadow-2xl shadow-black/40 p-8">
<form onSubmit={handleSubmit(onSubmit)} noValidate className="space-y-5">
{/* Name row */}
<div className="grid grid-cols-2 gap-4">
{/* First Name */}
<div className="flex flex-col gap-1.5">
<label htmlFor="firstName" className="text-xs font-semibold text-slate-300 uppercase tracking-wider">
First Name
</label>
<input
id="firstName"
type="text"
placeholder="Jane"
autoComplete="given-name"
aria-invalid={!!errors.firstName}
aria-describedby={errors.firstName ? "firstName-error" : undefined}
{...register("firstName")}
className={`w-full bg-slate-800/60 border rounded-xl px-4 py-3 text-white placeholder-slate-500 text-sm transition-all duration-200 outline-none focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/50 hover:border-slate-600 ${
errors.firstName ? "border-red-500/60 focus:ring-red-500/30" : "border-slate-700/60"
}`}
/>
{errors.firstName && (
<p id="firstName-error" role="alert" className="text-xs text-red-400 flex items-start gap-1 mt-0.5">
<span className="mt-0.5 shrink-0">⚠</span> {errors.firstName.message}
</p>
)}
</div>
{/* Last Name */}
<div className="flex flex-col gap-1.5">
<label htmlFor="lastName" className="text-xs font-semibold text-slate-300 uppercase tracking-wider">
Last Name
</label>
<input
id="lastName"
type="text"
placeholder="Doe"
autoComplete="family-name"
aria-invalid={!!errors.lastName}
aria-describedby={errors.lastName ? "lastName-error" : undefined}
{...register("lastName")}
className={`w-full bg-slate-800/60 border rounded-xl px-4 py-3 text-white placeholder-slate-500 text-sm transition-all duration-200 outline-none focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/50 hover:border-slate-600 ${
errors.lastName ? "border-red-500/60 focus:ring-red-500/30" : "border-slate-700/60"
}`}
/>
{errors.lastName && (
<p id="lastName-error" role="alert" className="text-xs text-red-400 flex items-start gap-1 mt-0.5">
<span className="mt-0.5 shrink-0">⚠</span> {errors.lastName.message}
</p>
)}
</div>
</div>
{/* Email */}
<div className="flex flex-col gap-1.5">
<label htmlFor="email" className="text-xs font-semibold text-slate-300 uppercase tracking-wider">
Business Email Address
</label>
<input
id="email"
type="email"
placeholder="jane.doe@company.com"
autoComplete="email"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? "email-error" : "email-hint"}
{...register("email")}
className={`w-full bg-slate-800/60 border rounded-xl px-4 py-3 text-white placeholder-slate-500 text-sm transition-all duration-200 outline-none focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/50 hover:border-slate-600 ${
errors.email ? "border-red-500/60 focus:ring-red-500/30" : "border-slate-700/60"
}`}
/>
{errors.email ? (
<p id="email-error" role="alert" className="text-xs text-red-400 flex items-start gap-1 mt-0.5">
<span className="mt-0.5 shrink-0">⚠</span> {errors.email.message}
</p>
) : (
<p id="email-hint" className="text-xs text-slate-500">
Personal email providers are not accepted.
</p>
)}
</div>
{/* Password */}
<div className="flex flex-col gap-1.5">
<label htmlFor="password" className="text-xs font-semibold text-slate-300 uppercase tracking-wider">
Password
</label>
<input
id="password"
type="password"
placeholder="Create a strong password"
autoComplete="new-password"
aria-invalid={!!errors.password}
aria-describedby={errors.password ? "password-error" : "password-hint"}
{...register("password")}
className={`w-full bg-slate-800/60 border rounded-xl px-4 py-3 text-white placeholder-slate-500 text-sm transition-all duration-200 outline-none focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/50 hover:border-slate-600 ${
errors.password ? "border-red-500/60 focus:ring-red-500/30" : "border-slate-700/60"
}`}
/>
{/* Strength indicator */}
{passwordValue && (
<div className="mt-1 space-y-1.5">
<div className="flex gap-1">
{[1, 2, 3, 4].map((i) => (
<div
key={i}
className={`h-1 flex-1 rounded-full transition-all duration-300 ${
i <= strength.level ? strength.color : "bg-slate-700"
}`}
/>
))}
</div>
{strength.label && (
<p className="text-xs text-slate-400">
Password strength: <span className="font-medium text-slate-300">{strength.label}</span>
</p>
)}
</div>
)}
{errors.password ? (
<p id="password-error" role="alert" className="text-xs text-red-400 flex items-start gap-1 mt-0.5">
<span className="mt-0.5 shrink-0">⚠</span> {errors.password.message}
</p>
) : (
<p id="password-hint" className="text-xs text-slate-500">
Minimum 8 characters — must include uppercase, number, and special character.
</p>
)}
</div>
{/* Confirm Password */}
<div className="flex flex-col gap-1.5">
<label htmlFor="confirmPassword" className="text-xs font-semibold text-slate-300 uppercase tracking-wider">
Confirm Password
</label>
<input
id="confirmPassword"
type="password"
placeholder="Re-enter your password"
autoComplete="new-password"
aria-invalid={!!errors.confirmPassword}
aria-describedby={errors.confirmPassword ? "confirmPassword-error" : undefined}
{...register("confirmPassword")}
className={`w-full bg-slate-800/60 border rounded-xl px-4 py-3 text-white placeholder-slate-500 text-sm transition-all duration-200 outline-none focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/50 hover:border-slate-600 ${
errors.confirmPassword ? "border-red-500/60 focus:ring-red-500/30" : "border-slate-700/60"
}`}
/>
{errors.confirmPassword && (
<p id="confirmPassword-error" role="alert" className="text-xs text-red-400 flex items-start gap-1 mt-0.5">
<span className="mt-0.5 shrink-0">⚠</span> {errors.confirmPassword.message}
</p>
)}
</div>
{/* Date of Birth */}
<div className="flex flex-col gap-1.5">
<label htmlFor="dateOfBirth" className="text-xs font-semibold text-slate-300 uppercase tracking-wider">
Date of Birth
</label>
<Controller
name="dateOfBirth"
control={control}
render={({ field }) => (
<input
id="dateOfBirth"
type="date"
max={getMaxDateOfBirth()}
min={getMinDateOfBirth()}
autoComplete="bday"
aria-invalid={!!errors.dateOfBirth}
aria-describedby={errors.dateOfBirth ? "dateOfBirth-error" : "dateOfBirth-hint"}
{...field}
className={`w-full bg-slate-800/60 border rounded-xl px-4 py-3 text-white placeholder-slate-500 text-sm transition-all duration-200 outline-none focus:ring-2 focus:ring-indigo-500/50 focus:border-indigo-500/50 hover:border-slate-600 [color-scheme:dark] ${
errors.dateOfBirth ? "border-red-500/60 focus:ring-red-500/30" : "border-slate-700/60"
}`}
/>
)}
/>
{errors.dateOfBirth ? (
<p id="dateOfBirth-error" role="alert" className="text-xs text-red-400 flex items-start gap-1 mt-0.5">
<span className="mt-0.5 shrink-0">⚠</span> {errors.dateOfBirth.message}
</p>
) : (
<p id="dateOfBirth-hint" className="text-xs text-slate-500">
You must be at least 18 years old to register.
</p>
)}
</div>
{/* Divider */}
<div className="border-t border-slate-700/50 pt-2" />
{/* Submit */}
<button
type="submit"
disabled={isSubmitting}
className="w-full bg-indigo-600 hover:bg-indigo-500 disabled:bg-indigo-800 disabled:cursor-not-allowed text-white font-semibold text-sm rounded-xl px-6 py-3.5 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-slate-900 shadow-lg shadow-indigo-500/20 hover:shadow-indigo-500/30 active:scale-[0.99]"
>
{isSubmitting ? (
<span className="flex items-center justify-center gap-2">
<svg className="w-4 h-4 animate-spin" viewBox="0 0 24 24" fill="none">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
Processing…
</span>
) : (
"Create Account"
)}
</button>
<p className="text-center text-xs text-slate-500 leading-relaxed">
By creating an account, you agree to our{" "}
<span className="text-slate-400 hover:text-white transition-colors cursor-pointer">Terms of Service</span>{" "}
and{" "}
<span className="text-slate-400 hover:text-white transition-colors cursor-pointer">Privacy Policy</span>.
</p>
</form>
</div>
</div>
</div>
);
}
By purchasing this prompt, you agree to our terms of service
CLAUDE-4-6-SONNET
Generate polished, production-ready React forms instantly. One prompt → full TypeScript component with Zod validation, React Hook Form, input masking, accessibility (WCAG AA), and beautiful Tailwind UI. Supports any language, complex rules (Luhn, PESEL, NIP, business email), and multi-field logic. No extra libraries needed. Tested on snadbox. You can quickly see the form in the sandbox I prepared, link in the manual
...more
Added over 1 month ago
