FormField
FormField
A wrapper component that enhances form controls with labels, helper text, and error messages.
import { FormField, Input, Box } from '@beauginbey/vanilla-components'; export default function App() { return ( <Box display="flex" gap={4} flexDirection="column"> <FormField label="Email Address" helperText="We'll never share your email" > <Input type="email" placeholder="name@example.com" /> </FormField> <FormField label="Password" required helperText="Must be at least 8 characters" > <Input type="password" placeholder="Enter password" /> </FormField> </Box> ); }
Props
label
- Field label texthelperText
- Helper text displayed below the fielderror
- Show error stateerrorMessage
- Error message to display (replaces helper text)required
- Mark field as requireddisabled
- Disable the field and all its childrenchildren
- Form control element (Input, Select, etc.)
Basic Usage
import { FormField, Input, Select, Checkbox, Box, Text } from '@beauginbey/vanilla-components'; export default function App() { return ( <Box display="flex" flexDirection="column" gap={4}> <Text size="lg" weight="semibold">Contact Information</Text> <FormField label="Full Name" required> <Input placeholder="John Doe" /> </FormField> <FormField label="Email" helperText="We'll use this to contact you" required > <Input type="email" placeholder="john@example.com" /> </FormField> <FormField label="Country"> <Select> <option value="">Select a country</option> <option value="us">United States</option> <option value="uk">United Kingdom</option> <option value="ca">Canada</option> </Select> </FormField> <FormField> <Checkbox label="Subscribe to newsletter" /> </FormField> </Box> ); }
With Validation
import { FormField, Input, Button, Box } from '@beauginbey/vanilla-components'; import { useState } from 'react'; export default function App() { const [values, setValues] = useState({ username: '', email: '', password: '' }); const [errors, setErrors] = useState({}); const handleChange = (field) => (e) => { setValues({ ...values, [field]: e.target.value }); // Clear error when user starts typing if (errors[field]) { setErrors({ ...errors, [field]: '' }); } }; const validate = () => { const newErrors = {}; if (!values.username) { newErrors.username = 'Username is required'; } else if (values.username.length < 3) { newErrors.username = 'Username must be at least 3 characters'; } if (!values.email) { newErrors.email = 'Email is required'; } else if (!/S+@S+.S+/.test(values.email)) { newErrors.email = 'Email is invalid'; } if (!values.password) { newErrors.password = 'Password is required'; } else if (values.password.length < 8) { newErrors.password = 'Password must be at least 8 characters'; } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = (e) => { e.preventDefault(); if (validate()) { alert('Form submitted successfully!'); } }; return ( <Box as="form" onSubmit={handleSubmit} display="flex" flexDirection="column" gap={3}> <FormField label="Username" required error={!!errors.username} errorMessage={errors.username} > <Input value={values.username} onChange={handleChange('username')} placeholder="Choose a username" /> </FormField> <FormField label="Email" required error={!!errors.email} errorMessage={errors.email} helperText="We'll send a verification email" > <Input type="email" value={values.email} onChange={handleChange('email')} placeholder="your@email.com" /> </FormField> <FormField label="Password" required error={!!errors.password} errorMessage={errors.password} helperText="Minimum 8 characters" > <Input type="password" value={values.password} onChange={handleChange('password')} placeholder="Create a strong password" /> </FormField> <Button type="submit" fullWidth> Create Account </Button> </Box> ); }
With Different Controls
import { FormField, Input, Select, Radio, Switch, Box, Text } from '@beauginbey/vanilla-components'; import { useState } from 'react'; export default function App() { const [formData, setFormData] = useState({ name: '', role: '', department: 'engineering', notifications: true }); return ( <Box display="flex" flexDirection="column" gap={4}> <Text size="lg" weight="semibold">Employee Information</Text> <FormField label="Full Name" required> <Input value={formData.name} onChange={(e) => setFormData({ ...formData, name: e.target.value })} placeholder="Enter your full name" /> </FormField> <FormField label="Job Role" helperText="Select your primary role" required > <Select value={formData.role} onChange={(e) => setFormData({ ...formData, role: e.target.value })} > <option value="">Choose a role</option> <option value="developer">Developer</option> <option value="designer">Designer</option> <option value="manager">Manager</option> <option value="analyst">Analyst</option> </Select> </FormField> <FormField label="Department"> <Box display="flex" flexDirection="column" gap={2}> <Radio name="dept" label="Engineering" value="engineering" checked={formData.department === 'engineering'} onChange={(e) => setFormData({ ...formData, department: e.target.value })} /> <Radio name="dept" label="Design" value="design" checked={formData.department === 'design'} onChange={(e) => setFormData({ ...formData, department: e.target.value })} /> <Radio name="dept" label="Marketing" value="marketing" checked={formData.department === 'marketing'} onChange={(e) => setFormData({ ...formData, department: e.target.value })} /> </Box> </FormField> <FormField> <Switch label="Email notifications" checked={formData.notifications} onChange={(e) => setFormData({ ...formData, notifications: e.target.checked })} /> </FormField> </Box> ); }
States
import { FormField, Input, Box, Text } from '@beauginbey/vanilla-components'; export default function App() { return ( <Box display="flex" flexDirection="column" gap={4}> <Box> <Text size="sm" weight="semibold">Normal State</Text> <Box marginBottom={2} /> <FormField label="Field Label" helperText="This is helper text" > <Input placeholder="Normal input" /> </FormField> </Box> <Box> <Text size="sm" weight="semibold">Required Field</Text> <Box marginBottom={2} /> <FormField label="Required Field" required helperText="This field is required" > <Input placeholder="Required input" /> </FormField> </Box> <Box> <Text size="sm" weight="semibold">Error State</Text> <Box marginBottom={2} /> <FormField label="Field with Error" error errorMessage="This field has an error" > <Input placeholder="Error input" /> </FormField> </Box> <Box> <Text size="sm" weight="semibold">Disabled State</Text> <Box marginBottom={2} /> <FormField label="Disabled Field" disabled helperText="This field is disabled" > <Input placeholder="Disabled input" /> </FormField> </Box> </Box> ); }
Complex Form Example
import { FormField, Input, Select, Checkbox, Button, Box, Text } from '@beauginbey/vanilla-components'; import { useState } from 'react'; export default function App() { const [formData, setFormData] = useState({ firstName: '', lastName: '', email: '', phone: '', company: '', jobTitle: '', country: '', acceptTerms: false }); const [errors, setErrors] = useState({}); const handleChange = (field) => (e) => { const value = e.target.type === 'checkbox' ? e.target.checked : e.target.value; setFormData({ ...formData, [field]: value }); // Clear error on change if (errors[field]) { setErrors({ ...errors, [field]: '' }); } }; const validate = () => { const newErrors = {}; if (!formData.firstName) newErrors.firstName = 'First name is required'; if (!formData.lastName) newErrors.lastName = 'Last name is required'; if (!formData.email) newErrors.email = 'Email is required'; else if (!/S+@S+.S+/.test(formData.email)) newErrors.email = 'Email is invalid'; if (!formData.country) newErrors.country = 'Please select a country'; if (!formData.acceptTerms) newErrors.acceptTerms = 'You must accept the terms'; setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = (e) => { e.preventDefault(); if (validate()) { alert('Registration successful!\n\n' + JSON.stringify(formData, null, 2)); } }; return ( <Box as="form" onSubmit={handleSubmit} display="flex" flexDirection="column" gap={4}> <Text size="xl" weight="semibold">Registration Form</Text> <Box display="grid" gridTemplateColumns="2" gap={3}> <FormField label="First Name" required error={!!errors.firstName} errorMessage={errors.firstName} > <Input value={formData.firstName} onChange={handleChange('firstName')} placeholder="John" /> </FormField> <FormField label="Last Name" required error={!!errors.lastName} errorMessage={errors.lastName} > <Input value={formData.lastName} onChange={handleChange('lastName')} placeholder="Doe" /> </FormField> </Box> <FormField label="Email Address" required error={!!errors.email} errorMessage={errors.email} helperText="We'll never share your email" > <Input type="email" value={formData.email} onChange={handleChange('email')} placeholder="john.doe@example.com" /> </FormField> <Box display="grid" gridTemplateColumns="2" gap={3}> <FormField label="Phone Number" helperText="Optional" > <Input type="tel" value={formData.phone} onChange={handleChange('phone')} placeholder="+1 (555) 123-4567" /> </FormField> <FormField label="Country" required error={!!errors.country} errorMessage={errors.country} > <Select value={formData.country} onChange={handleChange('country')} > <option value="">Select country</option> <option value="us">United States</option> <option value="ca">Canada</option> <option value="uk">United Kingdom</option> <option value="au">Australia</option> </Select> </FormField> </Box> <Box display="grid" gridTemplateColumns="2" gap={3}> <FormField label="Company"> <Input value={formData.company} onChange={handleChange('company')} placeholder="Acme Corp" /> </FormField> <FormField label="Job Title"> <Input value={formData.jobTitle} onChange={handleChange('jobTitle')} placeholder="Software Engineer" /> </FormField> </Box> <FormField error={!!errors.acceptTerms} errorMessage={errors.acceptTerms}> <Checkbox label="I accept the terms and conditions" checked={formData.acceptTerms} onChange={handleChange('acceptTerms')} /> </FormField> <Button type="submit" fullWidth size="lg"> Complete Registration </Button> </Box> ); }
TypeScript
FormField is fully typed with TypeScript:
import { FormField, FormFieldProps, Input } from '@beauginbey/vanilla-components';
// Type-safe props
const MyFormField = (props: FormFieldProps) => {
return <FormField {...props} />;
};
// Custom form field wrapper
interface CustomFieldProps extends FormFieldProps {
name: string;
value: string;
onChange: (value: string) => void;
}
const CustomField = ({ name, value, onChange, ...fieldProps }: CustomFieldProps) => {
return (
<FormField {...fieldProps}>
<Input
name={name}
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</FormField>
);
};
// Form with validation
interface FormData {
email: string;
password: string;
}
interface FormErrors {
email?: string;
password?: string;
}
const LoginForm = () => {
const [data, setData] = useState<FormData>({ email: '', password: '' });
const [errors, setErrors] = useState<FormErrors>({});
return (
<form>
<FormField
label="Email"
required
error={!!errors.email}
errorMessage={errors.email}
>
<Input
type="email"
value={data.email}
onChange={(e) => setData({ ...data, email: e.target.value })}
/>
</FormField>
</form>
);
};