053
LVL 02 — CIRCUIT BREAKER SESSION 053 DAY 53

FORM VALIDATION

🎫 PIXELCRAFT-041
Feature | 🟡 Medium | Priority: 🟡 Medium

The export dialog needs: filename input, format dropdown (PNG/JPEG/WebP), quality slider (JPEG only), dimension inputs with aspect ratio lock. All need real-time validation. Introduce Zod for schema validation.
CONCEPTS.UNLOCKED
🎛
Controlled Components
React manages form state. Every input's value is stored in state. Every change updates state. React is the single source of truth for what the form contains.
Real-Time Validation
Validate as the user types. Every keystroke triggers validation. Errors appear immediately, disappear when corrected. No waiting for form submission to see what's wrong.
🛡
Zod: Schema Validation
Define the shape of valid data, reject everything else. z.string().min(1).max(100) — declarative, composable, type-safe. One schema validates, transforms, and types.
Accessible Forms
Labels, aria attributes, error announcements. Every input has a label. Errors are announced to screen readers. Keyboard navigation works. Forms for everyone.
🎨
Form UX
Inline errors, field highlighting, disabled submit until valid. Red borders on invalid fields. Error messages below each field. Submit button greys out when validation fails.
🔗
Aspect Ratio Lock
Change width → height auto-adjusts to maintain ratio. A practical UX pattern that combines math with controlled component state management.
HANDS-ON.TASKS
01
Install Zod & Define the Schema
npm install zod import { z } from 'zod'; const exportSchema = z.object({ filename: z.string() .min(1, 'Filename is required') .max(100, 'Filename too long') .regex( /^[a-zA-Z0-9_\-. ]+$/, 'Only letters, numbers, dashes, ' + 'underscores, dots' ), format: z.enum(['png', 'jpeg', 'webp']), quality: z.number() .min(1).max(100).optional(), width: z.number().min(1).max(10000), height: z.number().min(1).max(10000), maintainAspectRatio: z.boolean(), });
The schema IS the validation. z.string().min(1) means "a string with at least 1 character." z.enum(['png', 'jpeg', 'webp']) means "exactly one of these values." Declarative and readable.
02
Build the Export Dialog
function ExportDialog({ image, onExport, onClose }) { const [formData, setFormData] = useState({ filename: image.name.replace(/\.[^.]+$/, ''), format: 'png', quality: 90, width: image.width, height: image.height, maintainAspectRatio: true, }); const [errors, setErrors] = useState({}); const validate = (data) => { const result = exportSchema.safeParse(data); if (result.success) { setErrors({}); return true; } const fieldErrors = {}; result.error.issues.forEach(issue => { fieldErrors[issue.path[0]] = issue.message; }); setErrors(fieldErrors); return false; }; const handleChange = (field, value) => { const updated = { ...formData, [field]: value }; // Aspect ratio lock if (field === 'width' && formData.maintainAspectRatio) { updated.height = Math.round( value * (image.height / image.width) ); } if (field === 'height' && formData.maintainAspectRatio) { updated.width = Math.round( value * (image.width / image.height) ); } setFormData(updated); validate(updated); }; const handleSubmit = () => { if (validate(formData)) onExport(formData); };
03
Build the Form UI
return ( <div className="...modal classes..."> <h2>Export Image</h2> <label htmlFor="filename">Filename</label> <input id="filename" value={formData.filename} onChange={(e) => handleChange('filename', e.target.value)} className={errors.filename ? 'border-red-500' : ''} /> {errors.filename && <p className="text-red-400 text-xs"> {errors.filename}</p>} <label htmlFor="format">Format</label> <select id="format" value={formData.format} onChange={e => handleChange('format', e.target.value)}> <option value="png"> PNG (lossless)</option> <option value="jpeg"> JPEG (smaller file)</option> <option value="webp"> WebP (modern)</option> </select> {formData.format === 'jpeg' && (<> <label> Quality: {formData.quality}% </label> <input type="range" min={1} max={100} value={formData.quality} onChange={e => handleChange( 'quality', parseInt(e.target.value) )} /> </>)} <button onClick={handleSubmit} disabled={ Object.keys(errors).length > 0 }> Export </button> </div> ); }
04
Test Edge Cases
InputExpected
Empty filename"Filename is required"
Special chars: file<>name"Only letters, numbers..."
Width = 0Number must be ≥ 1
Width = 99999Number must be ≤ 10000
JPEG quality = 0Number must be ≥ 1
All validExport button enabled
05
Close the Ticket
git switch -c feature/PIXELCRAFT-041-export-dialog git add src/ git commit -m "Add export dialog with Zod validation (PIXELCRAFT-041)" git push origin feature/PIXELCRAFT-041-export-dialog # PR → Review → Merge → Close ticket ✅
CS.DEEP-DIVE

Schema validation is "specify the shape of valid data, reject everything else."

"Never trust user input" is the first commandment of security and reliability.

// Validation at every boundary:

Database schemas  → define valid records
API schemas       → define valid requests
                    (OpenAPI / Swagger)
Type systems      → define valid values
                    (TypeScript)
Protocols         → define valid messages

// Validation at every boundary is the
// foundation of robust software.
"Validation Lab"
[A] Add a "preset" dropdown: Social Media (1080×1080), Desktop Wallpaper (1920×1080), Phone Wallpaper (1080×1920). Selecting a preset fills in width/height and locks aspect ratio.
[B] Add file size estimation: based on format + quality + dimensions, estimate the output file size before export. Display "~2.4 MB" that updates in real-time.
[C] Research React Hook Form + Zod integration. Refactor the export dialog to use React Hook Form. Compare: less boilerplate? Better performance? Easier validation?
REF.MATERIAL
ARTICLE
Colin McDonnell
Official Zod docs: primitives, objects, arrays, enums, transforms, refinements, error handling. The complete schema validation reference.
ZODOFFICIALESSENTIAL
VIDEO
Web Dev Simplified
Practical Zod walkthrough: defining schemas, parsing, safeParse, error handling, and integration with forms.
ZODPRACTICAL
ARTICLE
React Team
Controlled vs uncontrolled inputs, onChange handling, value binding, and form patterns in React.
FORMSOFFICIAL
ARTICLE
W3C
Accessible form patterns: labels, error messages, grouping, instructions, and validation announcements for screen readers.
ACCESSIBILITYFORMSSTANDARD
VIDEO
Fireship
Ultra-fast Zod overview: TypeScript-first validation, schema definition, parsing, and why it replaced Joi/Yup.
ZODQUICK
// LEAVE EXCITED BECAUSE
The export dialog is polished, validates in real-time, and handles every edge case. Zod makes validation declarative and reusable.