Validation

Provide valuable, actionable feedback to your users with HTML5 form validation, via browser default behaviors or custom styles and JavaScript.

Native HTML5 form validation

Bootstrap scopes the :valid and :invalid styles to parent .was-validated class, usually applied to the <Form> (you can use the validated prop as a shortcut). Otherwise, any required field without a value shows up as invalid on page load. This way, you may choose when to activate them (typically after form submission is attempted).

Looks good!
Looks good!
@
Please choose a username.
Please provide a valid city.
Please provide a valid state.
Please provide a valid zip.
You must agree before submitting.
function FormExample() {
const [validated, setValidated] = useState(false);
const handleSubmit = (event) => {
const form = event.currentTarget;
if (form.checkValidity() === false) {
event.preventDefault();
event.stopPropagation();
}
setValidated(true);
};
return (
<Form noValidate validated={validated} onSubmit={handleSubmit}>
<Row className="mb-3">
<Form.Group as={Col} md="4" controlId="validationCustom01">
<Form.Label>First name</Form.Label>
<Form.Control required type="text" placeholder="First name" defaultValue="Mark"/>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="4" controlId="validationCustom02">
<Form.Label>Last name</Form.Label>
<Form.Control required type="text" placeholder="Last name" defaultValue="Otto"/>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="4" controlId="validationCustomUsername">
<Form.Label>Username</Form.Label>
<InputGroup hasValidation>
<InputGroup.Text id="inputGroupPrepend">@</InputGroup.Text>
<Form.Control type="text" placeholder="Username" aria-describedby="inputGroupPrepend" required />
<Form.Control.Feedback type="invalid">
Please choose a username.
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
</Row>
<Row className="mb-3">
<Form.Group as={Col} md="6" controlId="validationCustom03">
<Form.Label>City</Form.Label>
<Form.Control type="text" placeholder="City" required />
<Form.Control.Feedback type="invalid">
Please provide a valid city.
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="3" controlId="validationCustom04">
<Form.Label>State</Form.Label>
<Form.Control type="text" placeholder="State" required />
<Form.Control.Feedback type="invalid">
Please provide a valid state.
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="3" controlId="validationCustom05">
<Form.Label>Zip</Form.Label>
<Form.Control type="text" placeholder="Zip" required />
<Form.Control.Feedback type="invalid">
Please provide a valid zip.
</Form.Control.Feedback>
</Form.Group>
</Row>
<Form.Group className="mb-3">
<Form.Check
required
label="Agree to terms and conditions"
feedback="You must agree before submitting."
feedbackType="invalid"
/>
</Form.Group>
<Button type="submit">Submit form</Button>
</Form>
);
}
render(<FormExample />);

Form libraries and server-rendered styles

It's often beneficial (especially in React) to handle form validation via a library like Formik, or react-formal. In those cases, isValid and isInvalid props can be added to form controls to manually apply validation styles. Below is a quick example integrating with Formik.

Looks good!
Looks good!
@
const { Formik } = formik;
const schema = yup.object().shape({
firstName: yup.string().required(),
lastName: yup.string().required(),
username: yup.string().required(),
city: yup.string().required(),
state: yup.string().required(),
zip: yup.string().required(),
terms: yup.bool().required().oneOf([true], 'Terms must be accepted'),
});
function FormExample() {
return (
<Formik validationSchema={schema} onSubmit={console.log}
initialValues={{
firstName: 'Mark',
lastName: 'Otto',
username: '',
city: '',
state: '',
zip: '',
terms: false,
}}
>
{({
handleSubmit,
handleChange,
handleBlur,
values,
touched,
isValid,
errors,
}) => (
<Form noValidate onSubmit={handleSubmit}>
<Row className="mb-3">
<Form.Group as={Col} md="4" controlId="validationFormik01">
<Form.Label>First name</Form.Label>
<Form.Control type="text" name="firstName" value={values.firstName} onChange={handleChange}
isValid={touched.firstName && !errors.firstName} />
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="4" controlId="validationFormik02">
<Form.Label>Last name</Form.Label>
<Form.Control type="text" name="lastName" value={values.lastName} onChange={handleChange} isValid={touched.lastName && !errors.lastName}/>
<Form.Control.Feedback>Looks good!</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="4" controlId="validationFormikUsername">
<Form.Label>Username</Form.Label>
<InputGroup hasValidation>
<InputGroup.Text id="inputGroupPrepend">@</InputGroup.Text>
<Form.Control type="text" placeholder="Username" aria-describedby="inputGroupPrepend"
name="username" value={values.username} onChange={handleChange} isInvalid={!!errors.username}/>
<Form.Control.Feedback type="invalid">
{errors.username}
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
</Row>
<Row className="mb-3">
<Form.Group as={Col} md="6" controlId="validationFormik03">
<Form.Label>City</Form.Label>
<Form.Control type="text" placeholder="City" name="city" value={values.city} onChange={handleChange} isInvalid={!!errors.city} />
<Form.Control.Feedback type="invalid">
{errors.city}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="3" controlId="validationFormik04">
<Form.Label>State</Form.Label>
<Form.Control type="text" placeholder="State" name="state" value={values.state} onChange={handleChange} isInvalid={!!errors.state} />
<Form.Control.Feedback type="invalid">
{errors.state}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="3" controlId="validationFormik05">
<Form.Label>Zip</Form.Label>
<Form.Control type="text" placeholder="Zip" name="zip" value={values.zip} onChange={handleChange} isInvalid={!!errors.zip} />
<Form.Control.Feedback type="invalid">
{errors.zip}
</Form.Control.Feedback>
</Form.Group>
</Row>
<Form.Group className="mb-3">
<Form.Check required name="terms" label="Agree to terms and conditions" onChange={handleChange}
isInvalid={!!errors.terms} feedback={errors.terms} feedbackType="invalid" id="validationFormik0" />
</Form.Group>
<Button type="submit">Submit form</Button>
</Form>
)}
</Formik>
);
}
render(<FormExample />);

Tooltips

If your form layout allows it, you can use the tooltip prop to display validation feedback in a styled tooltip. Be sure to have a parent with position: relative on it for tooltip positioning. In the example below, our column classes have this already, but your project may require an alternative setup.

Looks good!
Looks good!
@
const { Formik } = formik;
const schema = yup.object().shape({
firstName: yup.string().required(),
lastName: yup.string().required(),
username: yup.string().required(),
city: yup.string().required(),
state: yup.string().required(),
zip: yup.string().required(),
file: yup.mixed().required(),
terms: yup.bool().required().oneOf([true], 'terms must be accepted'),
});
function FormExample() {
return (
<Formik validationSchema={schema} onSubmit={console.log}
initialValues={{
firstName: 'Mark',
lastName: 'Otto',
username: '',
city: '',
state: '',
zip: '',
file: null,
terms: false,
}}
>
{({
handleSubmit,
handleChange,
handleBlur,
values,
touched,
isValid,
errors,
}) => (
<Form noValidate onSubmit={handleSubmit}>
<Row className="mb-3">
<Form.Group as={Col} md="4" controlId="validationFormik101" className="position-relative" >
<Form.Label>First name</Form.Label>
<Form.Control type="text" name="firstName" value={values.firstName} onChange={handleChange} isValid={touched.firstName && !errors.firstName} />
<Form.Control.Feedback tooltip>Looks good!</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="4" controlId="validationFormik102" className="position-relative">
<Form.Label>Last name</Form.Label>
<Form.Control type="text" name="lastName" value={values.lastName} onChange={handleChange} isValid={touched.lastName && !errors.lastName} />
<Form.Control.Feedback tooltip>Looks good!</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="4" controlId="validationFormikUsername2">
<Form.Label>Username</Form.Label>
<InputGroup hasValidation>
<InputGroup.Text id="inputGroupPrepend">@</InputGroup.Text>
<Form.Control type="text" placeholder="Username" aria-describedby="inputGroupPrepend" name="username"
value={values.username} onChange={handleChange} isInvalid={!!errors.username} />
<Form.Control.Feedback type="invalid" tooltip>
{errors.username}
</Form.Control.Feedback>
</InputGroup>
</Form.Group>
</Row>
<Row className="mb-3">
<Form.Group as={Col} md="6" controlId="validationFormik103" className="position-relative">
<Form.Label>City</Form.Label>
<Form.Control type="text" placeholder="City" name="city" value={values.city} onChange={handleChange} isInvalid={!!errors.city} />
<Form.Control.Feedback type="invalid" tooltip>
{errors.city}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="3" controlId="validationFormik104" className="position-relative">
<Form.Label>State</Form.Label>
<Form.Control type="text" placeholder="State" name="state" value={values.state} onChange={handleChange} isInvalid={!!errors.state}/>
<Form.Control.Feedback type="invalid" tooltip>
{errors.state}
</Form.Control.Feedback>
</Form.Group>
<Form.Group as={Col} md="3" controlId="validationFormik105" className="position-relative" >
<Form.Label>Zip</Form.Label>
<Form.Control type="text" placeholder="Zip" name="zip" value={values.zip} onChange={handleChange} isInvalid={!!errors.zip} />
<Form.Control.Feedback type="invalid" tooltip>
{errors.zip}
</Form.Control.Feedback>
</Form.Group>
</Row>
<Form.Group className="position-relative mb-3">
<Form.Label>File</Form.Label>
<Form.Control type="file" required name="file" onChange={handleChange} isInvalid={!!errors.file} />
<Form.Control.Feedback type="invalid" tooltip>
{errors.file}
</Form.Control.Feedback>
</Form.Group>
<Form.Group className="position-relative mb-3">
<Form.Check required name="terms" label="Agree to terms and conditions" onChange={handleChange} isInvalid={!!errors.terms}
feedback={errors.terms} feedbackType="invalid" id="validationFormik106" feedbackTooltip/>
</Form.Group>
<Button type="submit">Submit form</Button>
</Form>
)}
</Formik>
);
}
render(<FormExample />);

Input group validation

To properly show rounded corners in an <InputGroup> with validation, the <InputGroup> requires the hasValidation prop.

@
Please choose a username.
<InputGroup hasValidation>
<InputGroup.Text>@</InputGroup.Text>
<Form.Control type="text" required isInvalid />
<Form.Control.Feedback type="invalid">
Please choose a username.
</Form.Control.Feedback>
</InputGroup>