FormControl is a container component that wraps form inputs to provide consistent layout, labels, helper text, and error messaging. It automatically connects labels and error messages to form fields for better accessibility.
Basic usage
import { FormControl, TextField } from 'reshaped';
function Example() {
return (
<FormControl>
<FormControl.Label>Email address</FormControl.Label>
<TextField name="email" />
</FormControl>
);
}
With helper text
import { FormControl, TextField } from 'reshaped';
function HelperExample() {
return (
<FormControl>
<FormControl.Label>Email address</FormControl.Label>
<TextField name="email" placeholder="[email protected]" />
<FormControl.Helper>
We'll never share your email with anyone else.
</FormControl.Helper>
</FormControl>
);
}
With error state
import { FormControl, TextField } from 'reshaped';
import { useState } from 'react';
function ErrorExample() {
const [email, setEmail] = useState('');
const hasError = email.length > 0 && !email.includes('@');
return (
<FormControl hasError={hasError}>
<FormControl.Label>Email address</FormControl.Label>
<TextField
name="email"
value={email}
onChange={({ value }) => setEmail(value)}
/>
<FormControl.Helper>
Enter a valid email address.
</FormControl.Helper>
<FormControl.Error>
Please enter a valid email address.
</FormControl.Error>
</FormControl>
);
}
Required fields
import { FormControl, TextField } from 'reshaped';
<FormControl required>
<FormControl.Label>Full name</FormControl.Label>
<TextField name="fullName" />
</FormControl>
Disabled state
import { FormControl, TextField } from 'reshaped';
<FormControl disabled>
<FormControl.Label>Username</FormControl.Label>
<TextField name="username" />
<FormControl.Helper>
Username cannot be changed.
</FormControl.Helper>
</FormControl>
import { FormControl, TextField } from 'reshaped';
<FormControl size="medium">
<FormControl.Label>Medium size</FormControl.Label>
<TextField name="medium" size="medium" />
</FormControl>
<FormControl size="large">
<FormControl.Label>Large size</FormControl.Label>
<TextField name="large" size="large" />
</FormControl>
With different input types
import { FormControl, TextField, TextArea, Select, Checkbox, Switch } from 'reshaped';
import { View } from 'reshaped';
function InputTypesExample() {
return (
<View gap={6}>
<FormControl>
<FormControl.Label>Text input</FormControl.Label>
<TextField name="text" />
</FormControl>
<FormControl>
<FormControl.Label>Text area</FormControl.Label>
<TextArea name="description" />
</FormControl>
<FormControl>
<FormControl.Label>Select</FormControl.Label>
<Select.Custom name="option">
<Select.Option value="1">Option 1</Select.Option>
<Select.Option value="2">Option 2</Select.Option>
</Select.Custom>
</FormControl>
<FormControl>
<Checkbox name="agree">I agree to the terms</Checkbox>
</FormControl>
<FormControl>
<Switch name="notifications">Enable notifications</Switch>
</FormControl>
</View>
);
}
Group mode for radio/checkbox groups
import { FormControl, RadioGroup, Radio, View } from 'reshaped';
function GroupExample() {
return (
<FormControl group>
<FormControl.Label>Choose a plan</FormControl.Label>
<RadioGroup name="plan">
<View gap={3}>
<Radio value="free">Free</Radio>
<Radio value="pro">Pro</Radio>
<Radio value="enterprise">Enterprise</Radio>
</View>
</RadioGroup>
<FormControl.Helper>
You can upgrade or downgrade at any time.
</FormControl.Helper>
</FormControl>
);
}
Custom layout
import { FormControl, TextField, View } from 'reshaped';
function CustomLayoutExample() {
return (
<FormControl>
<View direction="row" gap={4} align="center">
<View width="150px">
<FormControl.Label>Amount</FormControl.Label>
</View>
<View.Item grow>
<TextField name="amount" />
</View.Item>
</View>
</FormControl>
);
}
Complete form example
import { FormControl, TextField, TextArea, Select, Button, View } from 'reshaped';
import { useState } from 'react';
function CompleteFormExample() {
const [formData, setFormData] = useState({
name: '',
email: '',
country: '',
message: '',
});
const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
// Validate and submit
};
return (
<form onSubmit={handleSubmit}>
<View gap={6}>
<FormControl required hasError={errors.name}>
<FormControl.Label>Full name</FormControl.Label>
<TextField
name="name"
value={formData.name}
onChange={({ value }) => setFormData({ ...formData, name: value })}
/>
<FormControl.Error>Name is required</FormControl.Error>
</FormControl>
<FormControl required hasError={errors.email}>
<FormControl.Label>Email address</FormControl.Label>
<TextField
name="email"
value={formData.email}
onChange={({ value }) => setFormData({ ...formData, email: value })}
/>
<FormControl.Helper>
We'll never share your email.
</FormControl.Helper>
<FormControl.Error>Please enter a valid email</FormControl.Error>
</FormControl>
<FormControl>
<FormControl.Label>Country</FormControl.Label>
<Select.Custom
name="country"
value={formData.country}
onChange={({ value }) => setFormData({ ...formData, country: value })}
>
<Select.Option value="us">United States</Select.Option>
<Select.Option value="uk">United Kingdom</Select.Option>
<Select.Option value="ca">Canada</Select.Option>
</Select.Custom>
</FormControl>
<FormControl>
<FormControl.Label>Message</FormControl.Label>
<TextArea
name="message"
value={formData.message}
onChange={({ value }) => setFormData({ ...formData, message: value })}
/>
</FormControl>
<Button type="submit">Submit</Button>
</View>
</form>
);
}
Accessibility
FormControl provides comprehensive accessibility support:
- Automatically generates unique IDs for proper label associations
- Connects helper text and error messages via
aria-describedby
- Adds
aria-invalid when hasError is true
- Marks required fields with
aria-required
- Uses semantic fieldset/legend for group mode
- Screen reader announces all associated content
Node for inserting children
Custom id for the form control. Auto-generated if not provided.
Component size, to be used together with the other form component sizes
Change component to show an error state and display FormControl.Error
Change component to show a required indicator
Apply disabled styles and propagate to child form elements
Apply semantic html markup when used for displaying multiple form fields together with a single label (e.g., RadioGroup, CheckboxGroup)
Sub-components
FormControl.Label
Label component for form fields.
Node for inserting the label text
FormControl.Helper
Helper text component for providing additional context.
Node for inserting the caption text
FormControl.Error
Error message component. Only visible when hasError is true.
Node for inserting the error message text