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
For native HTML form validation–available in all our supported browsers,
the :valid
and :invalid
pseudo selectors are used to apply validation
styles as well as display feedback messages.
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).
Browsers provide their own validation UI by default on form
s. You can disable the
default UI by adding the HTML noValidate
attribute to your <Form>
or <form>
element.
import { useState } from 'react';import Button from 'react-bootstrap/Button';import Col from 'react-bootstrap/Col';import Form from 'react-bootstrap/Form';import InputGroup from 'react-bootstrap/InputGroup';import Row from 'react-bootstrap/Row';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.Controlrequiredtype="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.Controlrequiredtype="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.Controltype="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.Checkrequiredlabel="Agree to terms and conditions"feedback="You must agree before submitting."feedbackType="invalid"/></Form.Group><Button type="submit">Submit form</Button></Form>);}export default 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.
import Button from 'react-bootstrap/Button';import Col from 'react-bootstrap/Col';import Form from 'react-bootstrap/Form';import InputGroup from 'react-bootstrap/InputGroup';import Row from 'react-bootstrap/Row';import * as formik from 'formik';import * as yup from 'yup';function FormExample() {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'),});return (<FormikvalidationSchema={schema}onSubmit={console.log}initialValues={{firstName: 'Mark',lastName: 'Otto',username: '',city: '',state: '',zip: '',terms: false,}}>{({ handleSubmit, handleChange, values, touched, errors }) => (<Form noValidate onSubmit={handleSubmit}><Row className="mb-3"><Form.Group as={Col} md="4" controlId="validationFormik01"><Form.Label>First name</Form.Label><Form.Controltype="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.Controltype="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.Controltype="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.Controltype="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.Controltype="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.Controltype="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.Checkrequiredname="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>);}export default 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.
import Button from 'react-bootstrap/Button';import Col from 'react-bootstrap/Col';import Form from 'react-bootstrap/Form';import InputGroup from 'react-bootstrap/InputGroup';import Row from 'react-bootstrap/Row';import * as formik from 'formik';import * as yup from 'yup';function FormExample() {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'),});return (<FormikvalidationSchema={schema}onSubmit={console.log}initialValues={{firstName: 'Mark',lastName: 'Otto',username: '',city: '',state: '',zip: '',file: null,terms: false,}}>{({ handleSubmit, handleChange, values, touched, errors }) => (<Form noValidate onSubmit={handleSubmit}><Row className="mb-3"><Form.Groupas={Col}md="4"controlId="validationFormik101"className="position-relative"><Form.Label>First name</Form.Label><Form.Controltype="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.Groupas={Col}md="4"controlId="validationFormik102"className="position-relative"><Form.Label>Last name</Form.Label><Form.Controltype="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.Controltype="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.Groupas={Col}md="6"controlId="validationFormik103"className="position-relative"><Form.Label>City</Form.Label><Form.Controltype="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.Groupas={Col}md="3"controlId="validationFormik104"className="position-relative"><Form.Label>State</Form.Label><Form.Controltype="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.Groupas={Col}md="3"controlId="validationFormik105"className="position-relative"><Form.Label>Zip</Form.Label><Form.Controltype="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.Controltype="file"requiredname="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.Checkrequiredname="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>);}export default FormExample;
Input group validation
To properly show rounded corners in an <InputGroup>
with validation,
the <InputGroup>
requires the hasValidation
prop.
import Form from 'react-bootstrap/Form';import InputGroup from 'react-bootstrap/InputGroup';function ValidationInputGroupExample() {return (<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>);}export default ValidationInputGroupExample;