Day 8: Bootstrap 4 Forms Tutorial and Examples

Hey! This is the 8th day of Bootstrap 4 💁🏻 Today we will use the elements we have learnt about in the previous 2 days to create Bootstrap 4 forms. We will learn about specific form classes, ways to align inputs in a form and how to validate the data once the users inputs it. Forms make the switch from a static website to a dynamic one, where the content is generated via input. I think it is vital to know how to use forms and important to learn the options you have.

The article has the following structure:

Let’s dig in.

bootstrap-4-form-carrot

Photo credit to Jonas Mosesson for his shot.

Bootstrap 4 Form Classes

Forms are made of labels, inputs, help elements and buttons. All the elements need to be placed in a <form> tag. To provide a structure, you can put group inputs, labels and help text into a .form-group. This will help you keep a hierarchy of the elements and also add a margin bottom to the group.

By default, .form-groups and .form-controls are displayed as blocks, so they will take 100% of the width of their parents and stack vertically.

Here is a basic login form:

dark

<form>
    <div class="form-group">
      <label for="email1">Email address</label>
      <input type="email" class="form-control" id="email1" aria-describedby="emailHelp" placeholder="Enter email">
      <small id="emailHelp" class="form-text text-muted">Your information is safe with us.</small>
    </div>
    <div class="form-group">
      <label for="password1">Password</label>
      <input type="password" class="form-control" id="password1" placeholder="Password">
    </div>
    <button type="submit" class="btn btn-info">Submit</button>
</form>

Side note: you can see that in order to create a form, you need to be familiar with inputs and buttons. Be sure to check out Bootstrap 4 buttons tutorial and Bootstrap 4 inputs tutorial if something is unclear.

You can use all the inputs from Day 7: Bootstrap 4 Inputs Tutorial and Examples inside a form:

dark

<form>
  <div class="form-group">
    <label for="email2">Email address</label>
    <input type="email" class="form-control" id="email2" aria-describedby="emailHelp" placeholder="Enter email">
  </div>
  
  <div class="form-group">
    <label for="password2">Password</label>
    <input type="password" class="form-control" id="password2" placeholder="Password">
  </div>
  
  <div class="form-group">
    <label for="description1">Description</label>
    <textarea class="form-control" id="description1" rows="3" placeholder="Write your description here..."></textarea>
  </div>
  
  <div class="form-group">
    <label for="group2">You can also add a picture</label>
    <div class="input-group" id="group2">
      <div class="input-group-prepend">
        <span class="input-group-text" id="addon2"><i class="far fa-file-image"></i></span>
      </div>
      <div class="custom-file">
        <input type="file" class="custom-file-input" id="file2" aria-describedby="addon2">
        <label class="custom-file-label" for="file2">Choose file</label>
      </div>
    </div>
  </div>
  
  <div class="custom-control custom-checkbox form-group">
      <input type="checkbox" class="custom-control-input" id="checkTerms">
      <label class="custom-control-label" for="checkTerms">I have read and understood the Terms & Conditions.</label>
  </div>
  <button type="submit" class="btn btn-info">Submit</button>
</form>

Bootstrap 4 Form Layouts

If you want to switch from the default stacked alignment, you have multiple options to position multiple elements horizontally.

Layouts Using the Grid System

One of the simplest way to position elements on a row is to use the grid system. Inputs are block elements, so they will have the width of their parent. That means that in order to size an input, you need to determine de width of its parent .col. We have described the grid system in detail on the  Bootstrap 4 Grid System tutorial. You can check out again the tutorial if something is not clear.

Here is an example of sizing inputs in a form with the grid system.

dark

<form>
  <div class="row">
    <div class="col form-group">
      <input type="text" class="form-control" placeholder="First name">
    </div>
    <div class="col form-group">
      <input type="text" class="form-control" placeholder="Last name">
    </div>
  </div>
  <div class="row">
    <div class="col-sm-12 col-md-4 form-group">
      <input type="text" class="form-control" placeholder="City">
    </div>            
     <div class="col-sm-12 col-md-4 form-group">
      <input type="text" class="form-control" placeholder="Zipcode">
    </div>  
    <div class="col-sm-12 col-md-4 form-group">
      <input type="text" class="form-control" placeholder="Country">
    </div>
  </div>
  <button type="submit" class="btn btn-info">Submit</button>
</form>

Layouts Using Form Row

Bootstrap 4 has a variation of the .row class called .form-row. This class reduces the gutters (the spaces between the columns). When used with a form, it makes it look more compact.

Here is the form above with the .form-row instead of .row:

dark

<form>
  <div class="form-row">
  ...
  </div>
  <div class="form-row">
  ...
  </div>
</form>

Layouts Using Flex Utilities

The default behaviour of the cols inside the .row or .form-row is to divide the space between them and cover the whole row. If you want a column to only take up as much space as it needs, you can use the .col-auto class.

dark

<form>
  <div class="form-row">
    <div class="col-auto form-group">
      <label class="sr-only" for="email3">Email</label>
      <input type="text" class="form-control" id="email3" placeholder="[email protected]">
    </div>
    <div class="col-auto form-group">
      <label class="sr-only" for="password3">Password</label>
      <input type="password" class="form-control" id="password3" placeholder="*****">
    </div>
    <div class="col-auto form-group">
          <div class="custom-control custom-checkbox">
              <input type="checkbox" class="custom-control-input" id="remember">
              <label class="custom-control-label" for="remember">Remember Me</label>
          </div>
    </div>
    <div class="col-auto">
      <button type="submit" class="btn btn-primary mb-2">Submit</button>
    </div>
  </div>
</form>

Since the .form-row class is a flex container, you can use the flex utilities that we learnt in Day 3: Bootstrap 4 Flex Tutorial and Examples with it. In our example, you can see the elements are vertically aligned to the top Because of this the checkbox looks a bit out of place. We can center them vertically (align them on the secondary axis) using the flex utility class .align-items-center.

dark

<form>
  <div class="form-row align-items-center">
  ...
  </div>
</form>

We can also align the elements on the main axis (horizontally):

dark

<form>
  <div class="form-row justify-content-center align-items-center">
  ...
  </div>
</form>

Or push an item to the right:

dark

<form>
  <div class="form-row">
    <div class="col form-group">
      <label class="sr-only" for="email3">Email</label>
      <input type="text" class="form-control" id="email3" placeholder="[email protected]">
    </div>
    <div class="col form-group">
      <label class="sr-only" for="password3">Password</label>
      <input type="password" class="form-control" id="password3" placeholder="*****">
    </div>
  </div>
  <div class="form-row align-items-center">
    <div class="col-auto form-group">
      <div class="custom-control custom-checkbox">
          <input type="checkbox" class="custom-control-input" id="remember">
          <label class="custom-control-label" for="remember">Remember Me</label>
      </div>
    </div>
    <div class="col-auto ml-auto">
      <button type="submit" class="btn btn-primary mb-2">Submit</button>
    </div>
  </div>
</form>

Bootstrap 4 Inline Forms

If you want to have a form on a single row, you can use .form-row and .col-auto like in the examples below, but there is another option. Adding the .form-inline class to your form will automatically give it a flex behaviour and the inputs inside will have the width set to auto starting with resolutions >=576px.

Here is how the form above looks with .form-inline:

dark

<form class="form-inline">
    <div class="form-group">
      <label class="sr-only" for="email4">Email</label>
      <input type="text" class="form-control" id="email4" placeholder="[email protected]">
    </div>
    <div class="form-group">
      <label class="sr-only" for="password4">Password</label>
      <input type="password" class="form-control" id="password4" placeholder="*****">
    </div>
    <div class="form-group">
      <div class="custom-control custom-checkbox">
          <input type="checkbox" class="custom-control-input" id="remember">
          <label class="custom-control-label" for="remember">Remember Me</label>
      </div>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

You can see that .form-inline has no default spacing for the elements inside. So we need to space and align items manually using the Bootstrap 4 spacing utilities. I have added right margins to the .form-groups using the .mr-2 class and this is how they look:

dark

<form class="form-inline">
    <div class="form-group mr-2">
      <label class="sr-only" for="email4">Email</label>
      <input type="text" class="form-control" id="email4" placeholder="[email protected]">
    </div>
    <div class="form-group mr-2">
      <label class="sr-only" for="password4">Password</label>
      <input type="password" class="form-control" id="password4" placeholder="*****">
    </div>
    <div class="form-group mr-2">
      <div class="custom-control custom-checkbox">
          <input type="checkbox" class="custom-control-input" id="remember">
          <label class="custom-control-label" for="remember">Remember Me</label>
      </div>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

Bootstrap 4 Horizontal Forms

Another style you could go for in your forms is to have the label and input on the same row: the label on the left and the input on the right. You can achieve this by making the .form-group a row and defining the width of the label and input with cols. The label will align by default to the top of the .row, so you need to add the .col-form-label for the label to be vertically centered.

Here is an example:

dark

<form>
  <div class="form-group row">
    <label for="email5" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" id="email5" placeholder="Email">
    </div>
  </div>
  <div class="form-group row">
    <label for="password5" class="col-sm-2 col-form-label">Password</label>
    <div class="col-sm-10">
      <input type="password" class="form-control" id="password5" placeholder="Password">
    </div>
  </div>
  <div class="form-group row">
    <div class="col-sm-10 offset-sm-2">
      <div class="form-check">
        <input class="form-check-input" type="checkbox" id="check5">
        <label class="form-check-label" for="check5">
          I have agree with the terms and conditions.
        </label>
      </div>
    </div>
  </div>
  <div class="form-group row">
    <div class="col-sm-10">
      <button type="submit" class="btn btn-primary">Sign in</button>
    </div>
  </div>
</form>

Sizing Labels

You may use inputs that are smaller or larger than the default version. In order to have the label match the input you will need to use the classes: .col-form-label-sm for smaller labels and .col-form-label-lg for bigger labels.

dark

<form>
  <div class="form-group row">
    <label for="inputSm" class="col-sm-2 col-form-label col-form-label-sm">Small</label>
    <div class="col-sm-10">
      <input type="email" class="form-control form-control-sm" id="inputSm" placeholder="col-form-label-sm">
    </div>
  </div>
  <div class="form-group row">
    <label for="input" class="col-sm-2 col-form-label">Default</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" id="input" placeholder="col-form-label">
    </div>
  </div>
  <div class="form-group row">
    <label for="inputLg" class="col-sm-2 col-form-label col-form-label-lg">Large</label>
    <div class="col-sm-10">
      <input type="email" class="form-control form-control-lg" id="inputLg" placeholder="col-form-label-lg">
    </div>
  </div>
</form>

Disabled Forms

If you want all the inputs in a form to be disabled, you can disable each one or you can add the disabled attribute on the fieldset containing all the inputs and they will automatically disabled.

dark

<form>
  <fieldset disabled>
    <div class="form-group">
      <label for="disabledTextInput">Disabled input</label>
      <input type="text" id="disabledTextInput" class="form-control" placeholder="Disabled input">
    </div>
    <div class="form-group">
      <label for="disabledSelect">Disabled select menu</label>
      <select id="disabledSelect" class="form-control">
        <option>Disabled select</option>
      </select>
    </div>
    <div class="form-check">
      <input class="form-check-input" type="checkbox" id="disabledFieldsetCheck" disabled>
      <label class="form-check-label" for="disabledFieldsetCheck">
        Can't check this
      </label>
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </fieldset>
</form>

Bootstrap 4 Form Validation

Once you have created your Bootstrap 4 form you need to think about ways you can easily specify to a user how to fill it up. You should try and describe what you are expecting the user to input. It is great if you can add help text specifying what inputs are required and what format they should have. If the user inputs data that is not valid or leaves a required field without a value, you need to provide actionable feedback. There are multiple ways to do this.

Default Browser Validation

The first way to provide feedback at form submission is to rely on the browser defaults. This is done without the use of any scripts.

You need to specify the rules you want the input to follow. If the entered data checks all of them, then it gets the pseudo-class :valid on submission and the form goes to the server. And otherwise the input gets the pseudo-class :invalid and the form is blocked from being sent to the server.

So, for example, if you added the required attribute to a field and it is empty on form submission, the form will not be sent to the server and a tooltip will appear next to the input with a message that the field needs to receive a value.

Here is a list with the attributes that you can use and their purpose:

Attribute When to Use It
required When you want to make an input required.
pattern=[regular_expression] When you want for the data in an input to match a specific pattern.
minlength=[size] When you want a text input to have at least a number of characters.
maxlength=[size] When you want a text input to have at most a number of characters.
min=[size] When you want a number input to be above a limit.
max=[size] When you want a number input to be below a limit.

Here is an example with multiple input validations:

dark

<form>
  <div class="form-group">
    <label for="name">Name*</label>
    <input type="text" class="form-control" id="name" required>
  </div>
  
  <div class="form-group">
    <label for="telephone">Telephone*</label>
    <input type="text" class="form-control" id="telephone" required minlength="7" maxlength="12">
  </div>
  
  <div class="form-group">
    <label for="address">Address</label>
    <input type="text" class="form-control" id="address" required>
  </div>
  <div class="form-group">
      <select class="custom-select" required>
        <option value="" selected>Select a menu*</option>
        <option value="1">Breakfast</option>
        <option value="2">Lunch</option>
        <option value="3">Dinner</option>
      </select>
  </div>
  
  <div class="form-control">
      <label>I need cutlery:</label>
        <div class="custom-control custom-radio">
            <input type="radio" id="yes" name="cutlery" class="custom-control-input" required>
            <label class="custom-control-label" for="yes">Yes</label>
        </div>
        <div class="custom-control">
            <input type="radio" id="no" name="cutlery" class="custom-control-input" required>
            <label class="custom-control-label" for="no">No</label>
        </div>
  </div>
  <button type="submit" class="btn btn-info">Submit</button>
</form>

Bootstrap 4 offers the possibility to overwrite the browser default validation. There are two options to do this: you can validate and offer feedback on the client-side (via Javascript) or on the server-side.

Bootstrap 4 Client Side Validation

In order to use the client-side validation, you need to add the novalidate attribute on the form, so the browser default behaviour is disabled (the focus on inputs and tooltips). When the submit button is clicked, you will need to add the .was-validated class to the form in order for the style for the inputs to appear depending on their pseudo-class (:valid or :invalid).

Since the browser tooltip is disabled, it’s best you provide custom messages. To provide a message for successful validation you need to add a div with the .valid-feedback class which contains a success message. And if the validation doesn’t success, you need an .invalid-feedback div that contains an error message. These messages are not visible when you load the form. They are visible only if the form has the .was-validated class (which gets added after trying to submit the form).

Here is a an example:

dark

<form class="needs-validation" novalidate>
  <div class="form-group">
    <label for="name">Name*</label>
    <input type="text" class="form-control" id="name" placeholder="Your name" required>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please add a name.
    </div>
  </div>

  <div class="form-group">
    <label for="telephone">Telephone*</label>
    <input type="text" class="form-control" id="telephone" placeholder="Your telephone number" required minlength="7" maxlength="12">
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please choose a valid telephone number (between 7 and 10 characters).
    </div>
  </div>

  <div class="form-group">
    <label for="address">Address</label>
    <input type="text" class="form-control" id="address" placeholder="Your delivery address" required>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please add an address.
    </div>
  </div>
  
  <div class="form-group">
    <select class="custom-select" required>
      <option value="">Select a menu*</option>
      <option value="1">Breakfast</option>
      <option value="2">Lunch</option>
      <option value="3">Dinner</option>
    </select>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please choose a menu.
    </div>
  </div>

  <div class="form-group">
    <label>I need cutlery:</label>
    <div class="custom-control custom-radio">
        <input type="radio" id="yes" name="cutlery" class="custom-control-input" required>
        <label class="custom-control-label" for="yes">Yes</label>
    </div>
    <div class="custom-control custom-radio">
        <input type="radio" id="no" name="cutlery" class="custom-control-input" required>
        <label class="custom-control-label" for="no">No</label>
        <div class="valid-feedback">
              Looks good!
        </div>
        <div class="invalid-feedback">
              Please specify if you need cutlery.
        </div>
    </div>
  </div>
  <button type="submit" class="btn btn-info">Submit</button>
</form>

You will need the following Javascript in order to overwrite the default behaviour for validation:

<script>
// Example starter JavaScript for disabling form submissions if there are invalid fields
(function() {
  'use strict';
  window.addEventListener('load', function() {
    // Fetch all the forms we want to apply custom Bootstrap validation styles to
    var forms = document.getElementsByClassName('needs-validation');
    // Loop over them and prevent submission
    var validation = Array.prototype.filter.call(forms, function(form) {
      form.addEventListener('submit', function(event) {
        if (form.checkValidity() === false) {
          event.preventDefault();
          event.stopPropagation();
        }
        form.classList.add('was-validated');
      }, false);
    });
  }, false);
})();
</script>

You can see I have added messages for both success and error. As an observation, you can’t have a different message for every validation that a field has. For example, you can’t have a message if the user doesn’t input anything on a field that is required and another message if the pattern doesn’t math the regex. You only have one error message, so be sure to write all the rules that have to be followed when the user inputs data.

Validation Using Bootstrap 4 Tooltips

There is an alternative design for the feedback messages. If you want them to have a coloured background, you need to change the .[valid/invalid]-feedback class with the .[valid-invalid]-tooltip class. The tooltips have absolute positioning, so their parent div (the .form-group) needs to have position: relative; Otherwise, all the messages will appear at the bottom of the form.

Here is how the tooltips messages look:

dark

<form class="needs-validation" novalidate>
  <div class="form-group">
    <label for="name">Name*</label>
    <input type="text" class="form-control" id="name" placeholder="Your name" required>
    <div class="valid-tooltip">
      Looks good!
    </div>
    <div class="invalid-tooltip">
      Please add a name.
    </div>
  </div>

  <div class="form-group">
    <label for="telephone">Telephone*</label>
    <input type="text" class="form-control" id="telephone" placeholder="Your telephone number" required minlength="7" maxlength="12">
    <div class="valid-tooltip">
      Looks good!
    </div>
    <div class="invalid-tooltip">
      Please choose a valid telephone number (between 7 and 10 characters).
    </div>
  </div>

  <div class="form-group">
    <label for="address">Address</label>
    <input type="text" class="form-control" id="address" placeholder="Your delivery address" required>
    <div class="valid-tooltip">
      Looks good!
    </div>
    <div class="invalid-tooltip">
      Please add an address.
    </div>
  </div>
  
  <div class="form-group">
    <select class="custom-select" required>
      <option value="">Select a menu*</option>
      <option value="1">Breakfast</option>
      <option value="2">Lunch</option>
      <option value="3">Dinner</option>
    </select>
    <div class="valid-tooltip">
      Looks good!
    </div>
    <div class="invalid-tooltip">
      Please choose a menu.
    </div>
  </div>

  <div class="form-group">
    <label>I need cutlery:</label>
    <div class="custom-control custom-radio">
        <input type="radio" id="yes" name="cutlery" class="custom-control-input" required>
        <label class="custom-control-label" for="yes">Yes</label>
    </div>
    <div class="custom-control custom-radio">
        <input type="radio" id="no" name="cutlery" class="custom-control-input" required>
        <label class="custom-control-label" for="no">No</label>
        <div class="valid-tooltip">
              Looks good!
        </div>
        <div class="invalid-tooltip">
              Please specify if you need cutlery.
        </div>
    </div>
  </div>
  <button type="submit" class="btn btn-info">Submit</button>
</form>

The error messages will overlap with other elements so you will need to write some CSS if you want to space out the elements in the form.

Bootstrap 4 Server Side Validation

If you want to do server-side validation, you will need to tell the user the errors he made after the form has been submitted to the server and the form is loaded again.

When you redirect the user back to the form with the data he entered, you will be able to show an input as valid with the class .is-valid and invalid with the class .is-invalid. The success or error message are shown the same way (with the .[valid/invalid]-feedback class of .[valid/invalid]-tooltip class).

dark

<form>
  <div class="form-group">
    <label for="name">Name*</label>
    <input type="text" class="form-control is-valid" id="name" placeholder="Your name" value="John Doe" required>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please add a name.
    </div>
  </div>

  <div class="form-group">
    <label for="telephone">Telephone*</label>
    <input type="text" class="form-control is-invalid" id="telephone" placeholder="Your telephone number" required minlength="7" maxlength="12">
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please choose a valid telephone number (between 7 and 10 characters).
    </div>
  </div>

  <div class="form-group">
    <label for="address">Address</label>
    <input type="text" class="form-control is-invalid" id="address" placeholder="Your delivery address" required>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please add an address.
    </div>
  </div>
  
  <div class="form-group">
    <select class="custom-select is-invalid" required>
      <option value="">Select a menu*</option>
      <option value="1">Breakfast</option>
      <option value="2">Lunch</option>
      <option value="3">Dinner</option>
    </select>
    <div class="valid-feedback">
      Looks good!
    </div>
    <div class="invalid-feedback">
      Please choose a menu.
    </div>
  </div>

  <div class="form-group">
    <label>I need cutlery:</label>
    <div class="custom-control custom-radio">
        <input type="radio" id="yes" name="cutlery" class="custom-control-input is-invalid" required>
        <label class="custom-control-label" for="yes">Yes</label>
    </div>
    <div class="custom-control custom-radio">
        <input type="radio" id="no" name="cutlery" class="custom-control-input is-invalid" required>
        <label class="custom-control-label" for="no">No</label>
        <div class="valid-feedback">
              Looks good!
        </div>
        <div class="invalid-feedback">
              Please specify if you need cutlery.
        </div>
    </div>
  </div>
  <button type="submit" class="btn btn-info">Submit</button>
</form>

This is a wrap on the Bootstrap 4 Forms. You can see and edit all the code used in this tutorial on CodePen. Please let me know if you want to create a Bootstrap 4 form and run into trouble. And let me know your thoughts in the comments below. I hope you are feeling pretty confident in your Bootstrap 4 abilities by now.

bootstrap-4-forms-further

Photo credit to Daryl Beaney for his shot.

Further Reading

If you have time left, be sure to check out the official Bootstrap documentation and more on browser validations:

Photo credit to Alex Kunchevsky for his shot.

Sharing is caring!

Cristina Conacel

Cristina is a web developer and the owner of BootstrapBay. She likes foxes, clean design, writing blog posts and creating themes that are useful to other developers.