Skip to Content
ContentRecipesExamples

Input + Select

import { ZodForm, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const schema = z.object({ name: z.string().min(2), country: z.string().min(1), }); export default function Example() { return ( <ZodForm config={{ schema, fields: [ FormFieldHelpers.input("name", "Name"), FormFieldHelpers.select("country", "Country", [ { label: "US", value: "US" }, { label: "CA", value: "CA" }, ]), ], }} onSubmit={(d) => console.log(d)} /> ); }

Radio group

FormFieldHelpers.radio("plan", "Plan", [ { label: "Free", value: "free" }, { label: "Pro", value: "pro" }, ]);

Checkbox + Conditional input

FormFieldHelpers.checkbox("enable", "Enable"); { type: "conditional", name: "email-cond", condition: (v) => v.enable === true, field: FormFieldHelpers.input("email", "Email"), }

File input

{ type: "file", name: "avatar", label: "Avatar", multiple: false, accept: "image/*" }

Date input

{ type: "date", name: "dob", label: "Date of Birth" }

Slider

{ type: "slider", name: "volume", label: "Volume", defaultValue: 50 }

Switch

{ type: "switch", name: "enabled", label: "Enabled", defaultValue: true }

Font picker (optional)

{ type: "fontPicker", name: "font", label: "Font" }

Content field (headers/questions)

// Simple header FormFieldHelpers.content("Section Header", "Description text") // Custom render FormFieldHelpers.content(null, null, { render: () => <div>Custom content</div> }) // Using builder createBasicFormBuilder() .input("name", "Name") .content("Personal Information") .input("email", "Email") .build()

Recipe Examples

Copy-paste examples for common form patterns and use cases.

Basic Contact Form

Input → Output

Context: Simple contact form with validation

Code:

import { ZodForm, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const schema = z.object({ name: z.string().min(2, "Name must be at least 2 characters"), email: z.string().email("Please enter a valid email"), message: z.string().min(10, "Message must be at least 10 characters"), }); export function ContactForm() { const handleSubmit = async (data) => { console.log("Form submitted:", data); // Handle form submission }; return ( <ZodForm config={{ schema, fields: [ FormFieldHelpers.input("name", "Name"), FormFieldHelpers.input("email", "Email", "email"), FormFieldHelpers.textarea("message", "Message"), ], }} onSubmit={handleSubmit} title="Contact Us" subtitle="Send us a message and we'll get back to you" /> ); }

Expected Result: A contact form with name, email, and message fields with validation.

User Registration Form

Input → Output

Context: User registration with password confirmation

Code:

import { ZodForm, FormFieldHelpers, createPasswordSchema } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const schema = z.object({ firstName: z.string().min(2, "First name must be at least 2 characters"), lastName: z.string().min(2, "Last name must be at least 2 characters"), email: z.string().email("Please enter a valid email"), password: createPasswordSchema({ minLength: 8, requireUppercase: true, requireNumbers: true, }), confirmPassword: z.string(), terms: z.boolean().refine(val => val === true, "You must agree to the terms"), }).refine(data => data.password === data.confirmPassword, { message: "Passwords don't match", path: ["confirmPassword"], }); export function RegistrationForm() { const handleSubmit = async (data) => { console.log("User registered:", data); // Handle registration }; return ( <ZodForm config={{ schema, fields: [ FormFieldHelpers.input("firstName", "First Name"), FormFieldHelpers.input("lastName", "Last Name"), FormFieldHelpers.input("email", "Email", "email"), FormFieldHelpers.input("password", "Password", "password"), FormFieldHelpers.input("confirmPassword", "Confirm Password", "password"), FormFieldHelpers.checkbox("terms", "I agree to the terms and conditions"), ], }} onSubmit={handleSubmit} title="Create Account" subtitle="Join our community today" /> ); }

Expected Result: A registration form with password validation and terms agreement.

Dynamic Address Form

Input → Output

Context: Form with dynamic address fields

Code:

import { ZodForm, FieldArrayField, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const addressSchema = z.object({ street: z.string().min(1, "Street is required"), city: z.string().min(1, "City is required"), state: z.string().min(1, "State is required"), zipCode: z.string().min(5, "ZIP code must be at least 5 characters"), }); const schema = z.object({ name: z.string().min(2, "Name is required"), addresses: z.array(addressSchema).min(1, "At least one address is required"), }); export function AddressForm() { const handleSubmit = async (data) => { console.log("Addresses submitted:", data); // Handle form submission }; return ( <ZodForm config={{ schema, fields: [ FormFieldHelpers.input("name", "Name"), FieldArrayField({ name: "addresses", fields: [ FormFieldHelpers.input("street", "Street Address"), FormFieldHelpers.input("city", "City"), FormFieldHelpers.input("state", "State"), FormFieldHelpers.input("zipCode", "ZIP Code"), ], addButtonText: "Add Address", removeButtonText: "Remove Address", min: 1, max: 5, }), ], }} onSubmit={handleSubmit} title="Address Information" subtitle="Add your addresses" /> ); }

Expected Result: A form with dynamic address fields that can be added/removed.

Conditional Survey Form

Input → Output

Context: Survey form with conditional questions

Code:

import { ZodForm, ConditionalField, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const schema = z.object({ age: z.number().min(18, "You must be at least 18 years old"), hasExperience: z.boolean(), experience: z.string().optional(), isStudent: z.boolean(), schoolName: z.string().optional(), interests: z.array(z.string()).min(1, "Select at least one interest"), }); export function SurveyForm() { const handleSubmit = async (data) => { console.log("Survey submitted:", data); // Handle form submission }; return ( <ZodForm config={{ schema, fields: [ FormFieldHelpers.input("age", "Age", "number"), FormFieldHelpers.checkbox("hasExperience", "Do you have experience in this field?"), ConditionalField({ name: "experience", label: "Please describe your experience", type: "textarea", condition: (values) => values.hasExperience === true, }), FormFieldHelpers.checkbox("isStudent", "Are you currently a student?"), ConditionalField({ name: "schoolName", label: "School Name", type: "input", condition: (values) => values.isStudent === true, }), FormFieldHelpers.select("interests", "Interests", [ { label: "Select interests", value: "" }, { label: "Technology", value: "technology" }, { label: "Design", value: "design" }, { label: "Business", value: "business" }, ]), ], }} onSubmit={handleSubmit} title="Survey" subtitle="Help us understand your background" /> ); }

Expected Result: A survey form with conditional questions that appear based on user responses.

Product Configuration Form

Input → Output

Context: Product configuration with multiple field types

Code:

import { ZodForm, FormFieldHelpers, DynamicSectionField } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const schema = z.object({ productName: z.string().min(1, "Product name is required"), category: z.string().min(1, "Category is required"), price: z.number().min(0, "Price must be positive"), isDigital: z.boolean(), file: z.any().optional(), description: z.string().min(10, "Description must be at least 10 characters"), tags: z.array(z.string()).min(1, "At least one tag is required"), isActive: z.boolean(), startDate: z.date().optional(), endDate: z.date().optional(), }); export function ProductConfigForm() { const handleSubmit = async (data) => { console.log("Product configured:", data); // Handle form submission }; return ( <ZodForm config={{ schema, fields: [ FormFieldHelpers.input("productName", "Product Name"), FormFieldHelpers.select("category", "Category", [ { label: "Select category", value: "" }, { label: "Electronics", value: "electronics" }, { label: "Clothing", value: "clothing" }, { label: "Books", value: "books" }, ]), FormFieldHelpers.input("price", "Price", "number"), FormFieldHelpers.switch("isDigital", "Digital Product"), DynamicSectionField({ name: "digitalSection", title: "Digital Product Settings", condition: (values) => values.isDigital === true, fields: [ FormFieldHelpers.file("file", "Upload File", { accept: ".pdf,.doc,.docx", multiple: false, }), ], }), FormFieldHelpers.textarea("description", "Description"), FormFieldHelpers.select("tags", "Tags", [ { label: "Select tags", value: "" }, { label: "New", value: "new" }, { label: "Popular", value: "popular" }, { label: "Sale", value: "sale" }, ]), FormFieldHelpers.switch("isActive", "Active"), DynamicSectionField({ name: "scheduleSection", title: "Schedule Settings", condition: (values) => values.isActive === true, fields: [ FormFieldHelpers.date("startDate", "Start Date"), FormFieldHelpers.date("endDate", "End Date"), ], }), ], }} onSubmit={handleSubmit} title="Product Configuration" subtitle="Configure your product settings" layout="grid" columns={2} /> ); }

Expected Result: A comprehensive product configuration form with conditional sections.

Settings Form with Validation

Input → Output

Context: User settings form with complex validation

Code:

import { ZodForm, FormFieldHelpers, createEmailSchema, createPhoneSchema } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const schema = z.object({ // Personal Information firstName: z.string().min(2, "First name must be at least 2 characters"), lastName: z.string().min(2, "Last name must be at least 2 characters"), email: createEmailSchema("Email address"), phone: createPhoneSchema("Phone number").optional(), // Preferences theme: z.enum(["light", "dark", "system"], { required_error: "Please select a theme", }), language: z.string().min(1, "Language is required"), timezone: z.string().min(1, "Timezone is required"), // Notifications emailNotifications: z.boolean(), pushNotifications: z.boolean(), smsNotifications: z.boolean(), // Privacy profileVisibility: z.enum(["public", "private", "friends"]), dataSharing: z.boolean(), // Security twoFactorAuth: z.boolean(), sessionTimeout: z.number().min(5).max(480), // 5 minutes to 8 hours }); export function SettingsForm() { const handleSubmit = async (data) => { console.log("Settings updated:", data); // Handle form submission }; return ( <ZodForm config={{ schema, fields: [ // Personal Information Section FormFieldHelpers.input("firstName", "First Name"), FormFieldHelpers.input("lastName", "Last Name"), FormFieldHelpers.input("email", "Email", "email"), FormFieldHelpers.input("phone", "Phone", "tel"), // Preferences Section FormFieldHelpers.select("theme", "Theme", [ { label: "Light", value: "light" }, { label: "Dark", value: "dark" }, { label: "System", value: "system" }, ]), FormFieldHelpers.select("language", "Language", [ { label: "English", value: "en" }, { label: "Spanish", value: "es" }, { label: "French", value: "fr" }, ]), FormFieldHelpers.select("timezone", "Timezone", [ { label: "UTC-8 (PST)", value: "UTC-8" }, { label: "UTC-5 (EST)", value: "UTC-5" }, { label: "UTC+0 (GMT)", value: "UTC+0" }, ]), // Notifications Section FormFieldHelpers.checkbox("emailNotifications", "Email Notifications"), FormFieldHelpers.checkbox("pushNotifications", "Push Notifications"), FormFieldHelpers.checkbox("smsNotifications", "SMS Notifications"), // Privacy Section FormFieldHelpers.select("profileVisibility", "Profile Visibility", [ { label: "Public", value: "public" }, { label: "Private", value: "private" }, { label: "Friends Only", value: "friends" }, ]), FormFieldHelpers.checkbox("dataSharing", "Allow data sharing for analytics"), // Security Section FormFieldHelpers.checkbox("twoFactorAuth", "Enable Two-Factor Authentication"), FormFieldHelpers.slider("sessionTimeout", "Session Timeout (minutes)", { min: 5, max: 480, step: 5, }), ], }} onSubmit={handleSubmit} title="Account Settings" subtitle="Manage your account preferences" layout="grid" columns={2} showResetButton={true} resetButtonText="Reset to Defaults" /> ); }

Expected Result: A comprehensive settings form with multiple sections and complex validation.

Multi-Step Wizard Form

Input → Output

Context: Multi-step form wizard for complex data collection

Code:

import { useState } from "react"; import { ZodForm, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const step1Schema = z.object({ firstName: z.string().min(2, "First name is required"), lastName: z.string().min(2, "Last name is required"), email: z.string().email("Valid email is required"), }); const step2Schema = z.object({ company: z.string().min(1, "Company is required"), position: z.string().min(1, "Position is required"), experience: z.number().min(0, "Experience must be positive"), }); const step3Schema = z.object({ interests: z.array(z.string()).min(1, "Select at least one interest"), goals: z.string().min(10, "Goals must be at least 10 characters"), newsletter: z.boolean(), }); const steps = [ { title: "Personal Information", schema: step1Schema, fields: [ FormFieldHelpers.input("firstName", "First Name"), FormFieldHelpers.input("lastName", "Last Name"), FormFieldHelpers.input("email", "Email", "email"), ], }, { title: "Professional Information", schema: step2Schema, fields: [ FormFieldHelpers.input("company", "Company"), FormFieldHelpers.input("position", "Position"), FormFieldHelpers.input("experience", "Years of Experience", "number"), ], }, { title: "Preferences", schema: step3Schema, fields: [ FormFieldHelpers.select("interests", "Interests", [ { label: "Select interests", value: "" }, { label: "Technology", value: "technology" }, { label: "Design", value: "design" }, { label: "Business", value: "business" }, ]), FormFieldHelpers.textarea("goals", "Career Goals"), FormFieldHelpers.checkbox("newsletter", "Subscribe to newsletter"), ], }, ]; export function WizardForm() { const [currentStep, setCurrentStep] = useState(0); const [formData, setFormData] = useState({}); const handleStepSubmit = async (data) => { const newFormData = { ...formData, ...data }; setFormData(newFormData); if (currentStep < steps.length - 1) { setCurrentStep(currentStep + 1); } else { console.log("Wizard completed:", newFormData); // Handle final submission } }; const handlePrevious = () => { if (currentStep > 0) { setCurrentStep(currentStep - 1); } }; const currentStepData = steps[currentStep]; return ( <div className="max-w-2xl mx-auto"> <div className="mb-8"> <h1 className="text-3xl font-bold mb-2">Registration Wizard</h1> <p className="text-gray-600"> Step {currentStep + 1} of {steps.length}: {currentStepData.title} </p> <div className="w-full bg-gray-200 rounded-full h-2 mt-4"> <div className="bg-blue-600 h-2 rounded-full transition-all duration-300" style={{ width: `${((currentStep + 1) / steps.length) * 100}%` }} /> </div> </div> <ZodForm key={currentStep} // Force re-render on step change config={{ schema: currentStepData.schema, fields: currentStepData.fields, defaultValues: formData, }} onSubmit={handleStepSubmit} title={currentStepData.title} submitButtonText={currentStep === steps.length - 1 ? "Complete" : "Next"} showResetButton={false} /> {currentStep > 0 && ( <button type="button" onClick={handlePrevious} className="mt-4 px-4 py-2 text-gray-600 hover:text-gray-800" > ← Previous </button> )} </div> ); }

Expected Result: A multi-step wizard form with progress indicator and navigation.

Form with Server Error Handling

Input → Output

Context: Form with comprehensive server error handling

Code:

import { ZodForm, FormFieldHelpers, applyServerErrors } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; import { useForm } from "react-hook-form"; const schema = z.object({ username: z.string().min(3, "Username must be at least 3 characters"), email: z.string().email("Please enter a valid email"), password: z.string().min(8, "Password must be at least 8 characters"), }); export function ServerErrorForm() { const form = useForm({ resolver: zodResolver(schema), }); const handleSubmit = async (data) => { try { const response = await fetch("/api/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); if (!response.ok) { const errorData = await response.json(); if (errorData.fieldErrors) { // Apply field-specific errors applyServerErrors(form, errorData.fieldErrors); } else if (errorData.message) { // Set form-level error form.setError("root", { message: errorData.message }); } return; } // Success console.log("Registration successful"); } catch (error) { console.error("Registration error:", error); form.setError("root", { message: "Network error. Please try again." }); } }; return ( <ZodForm config={{ schema, fields: [ FormFieldHelpers.input("username", "Username"), FormFieldHelpers.input("email", "Email", "email"), FormFieldHelpers.input("password", "Password", "password"), ], }} onSubmit={handleSubmit} title="Create Account" subtitle="Join our community" errorDisplay="inline" /> ); }

Expected Result: A form that handles both client and server-side validation errors gracefully.

Next.js Server Action Form

Input → Output

Context: Form using Next.js Server Actions for server-side handling

Code:

import { ServerActionForm, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { signup } from "@/app/actions/auth"; export function SignupForm() { return ( <ServerActionForm action={signup} fields={[ FormFieldHelpers.input("name", "Name"), FormFieldHelpers.input("email", "Email", "email"), FormFieldHelpers.input("password", "Password", "password"), ]} title="Create Account" subtitle="Sign up to get started" /> ); }

Server Action (app/actions/auth.ts):

"use server"; import { z } from "zod"; const signupSchema = z.object({ name: z.string().min(2), email: z.string().email(), password: z.string().min(8), }); export async function signup( prevState: any, formData: FormData ): Promise<{ errors?: Record<string, string[]>; success?: boolean }> { const rawData = { name: formData.get("name"), email: formData.get("email"), password: formData.get("password"), }; const result = signupSchema.safeParse(rawData); if (!result.success) { return { errors: result.error.flatten().fieldErrors, }; } // Process signup await createUser(result.data); return { success: true }; }

Expected Result: A form that submits to a Next.js Server Action with automatic error handling.

Settings Form

Input → Output

Context: User settings form with multiple sections and preferences

Code:

import { ZodForm, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const settingsSchema = z.object({ // Personal firstName: z.string().min(2), lastName: z.string().min(2), email: z.string().email(), phone: z.string().optional(), // Preferences theme: z.enum(["light", "dark", "system"]), language: z.string().min(1), timezone: z.string().min(1), // Notifications emailNotifications: z.boolean(), pushNotifications: z.boolean(), smsNotifications: z.boolean(), }); export function SettingsForm() { return ( <ZodForm config={{ schema: settingsSchema, fields: [ // Personal Information FormFieldHelpers.input("firstName", "First Name"), FormFieldHelpers.input("lastName", "Last Name"), FormFieldHelpers.input("email", "Email", "email"), FormFieldHelpers.input("phone", "Phone", "tel"), // Preferences FormFieldHelpers.select("theme", "Theme", [ { label: "Light", value: "light" }, { label: "Dark", value: "dark" }, { label: "System", value: "system" }, ]), FormFieldHelpers.select("language", "Language", [ { label: "English", value: "en" }, { label: "Spanish", value: "es" }, { label: "French", value: "fr" }, ]), FormFieldHelpers.select("timezone", "Timezone", [ { label: "UTC-8 (PST)", value: "UTC-8" }, { label: "UTC-5 (EST)", value: "UTC-5" }, { label: "UTC+0 (GMT)", value: "UTC+0" }, ]), // Notifications FormFieldHelpers.checkbox("emailNotifications", "Email Notifications"), FormFieldHelpers.checkbox("pushNotifications", "Push Notifications"), FormFieldHelpers.checkbox("smsNotifications", "SMS Notifications"), ], }} onSubmit={async (data) => { await updateSettings(data); }} title="Account Settings" layout="grid" columns={2} showResetButton={true} /> ); }

Expected Result: A comprehensive settings form with multiple sections in a grid layout.

Search/Filter Form

Input → Output

Context: Search and filter form for data tables or listings

Code:

import { ZodForm, FormFieldHelpers } from "@rachelallyson/hero-hook-form"; import { z } from "zod"; const searchSchema = z.object({ query: z.string().optional(), category: z.string().optional(), minPrice: z.number().optional(), maxPrice: z.number().optional(), inStock: z.boolean().optional(), sortBy: z.enum(["price", "name", "date"]).optional(), }); export function SearchForm({ onSearch }: { onSearch: (filters: any) => void }) { return ( <ZodForm config={{ schema: searchSchema, fields: [ FormFieldHelpers.input("query", "Search", "text"), FormFieldHelpers.select("category", "Category", [ { label: "All Categories", value: "" }, { label: "Electronics", value: "electronics" }, { label: "Clothing", value: "clothing" }, { label: "Books", value: "books" }, ]), FormFieldHelpers.input("minPrice", "Min Price", "number"), FormFieldHelpers.input("maxPrice", "Max Price", "number"), FormFieldHelpers.checkbox("inStock", "In Stock Only"), FormFieldHelpers.select("sortBy", "Sort By", [ { label: "Price", value: "price" }, { label: "Name", value: "name" }, { label: "Date", value: "date" }, ]), ], }} onSubmit={onSearch} title="Search & Filter" layout="grid" columns={3} submitButtonText="Search" /> ); }

Expected Result: A search and filter form with multiple filter options in a grid layout.

Last updated on