Form Demo

This page showcases a comprehensive form with various input types, demonstrating form validation and user interaction patterns.

Form Validation Features

This demo illustrates Bootstrap's custom form validation with the following enhancements:

Clean Validation Approach

  • All form fields: Maintain their default appearance regardless of validation state
  • Smart feedback: Only show error messages for invalid fields, no success messages for valid fields
  • Clean design: Form controls stay neutral, labels turn red only when there are errors

Input Types Demonstrated

  • Text inputs (first name, last name, username)
  • Email validation
  • Password field
  • Number input with min/max validation
  • Date picker
  • Select dropdown
  • Radio buttons (gender selection)
  • Range slider with real-time feedback
  • Color picker
  • Switch toggle (email notifications)
  • File upload
  • Textarea
  • Checkbox agreement

Validation Behavior

  • Client-side validation: Immediate feedback using HTML5 and Bootstrap
  • Clean form controls: All form fields maintain their default appearance
  • Error-focused feedback: Only invalid fields show red labels and error messages
  • Best practice: No success messages or green styling - clean and minimal approach

Implementation Guide

HTML Structure

<form class="needs-validation" novalidate>
  <!-- Required field -->
  <input type="text" class="form-control" required />

  <!-- Optional field -->
  <input type="text" class="form-control" />
</form>

JavaScript Validation

const forms = document.querySelectorAll('.needs-validation');
Array.from(forms).forEach((form) => {
  form.addEventListener(
    'submit',
    (event) => {
      if (!form.checkValidity()) {
        event.preventDefault();
        event.stopPropagation();
      }
      form.classList.add('was-validated');
    },
    false
  );
});

Best Practices

Recommended approach: This form demonstrates clean validation patterns:

  • No success feedback: Avoid green checkmarks and "Looks good!" messages
  • Error-only feedback: Only show red styling and messages for problems
  • Clean interface: Valid fields remain visually neutral and uncluttered
  • User-friendly: Reduces visual noise and focuses attention on actual issues

⚠️ Production Notes: The modal showing JSON data is for demonstration purposes only. In production:

  • Replace modal display with actual form submission to your server
  • Add proper server-side validation and security measures

Complete Form Example

Personal Information
Please provide a valid first name.
Please provide a valid last name.
@
Please choose a username.
Please provide a valid email.
Please provide a valid password.
Additional Information
Please provide a valid age (18-120).
Please select a valid date.
Please provide a valid city.
Please select a valid state.
Please provide a valid zip.
Preferences
Please select a gender.
5
Please select your experience level.
Please select a color.
Please configure your notification preference.
Media
Please select a valid image file.
About You
Please provide a brief bio.
Special Fields
This field is disabled and cannot be edited.
This field is readonly and cannot be edited.
Review & Submit
You must agree before submitting.

By submitting this form, you agree to our privacy policy and terms of service.

<form class="needs-validation" novalidate>
  <div class="row">
    <!-- Left Column - Main Form Content -->
    <div class="col-lg-8">
      <!-- Personal Information Card -->
      <div class="card mb-4">
        <div class="card-header">
          <h6>Personal Information</h6>
        </div>
        <div class="card-body">
          <div class="row g-3">
            <div class="col-md-6">
              <label for="validationCustom01" class="form-label">First name</label>
              <input type="text" class="form-control" id="validationCustom01" name="firstName" value="John" required>
              <div class="invalid-feedback">
                Please provide a valid first name.
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustom02" class="form-label">Last name</label>
              <input type="text" class="form-control" id="validationCustom02" name="lastName" value="Doe" required>
              <div class="invalid-feedback">
                Please provide a valid last name.
              </div>
            </div>
            <div class="col-12">
              <label for="validationCustomUsername" class="form-label">Username</label>
              <div class="input-group has-validation">
                <span class="input-group-text" id="inputGroupPrepend">@</span>
                <input type="text" class="form-control" id="validationCustomUsername" name="username" aria-describedby="inputGroupPrepend" required>
                <div class="invalid-feedback">
                  Please choose a username.
                </div>
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustomEmail" class="form-label">Email</label>
              <input type="email" class="form-control" id="validationCustomEmail" name="email" placeholder="name@example.com" required>
              <div class="invalid-feedback">
                Please provide a valid email.
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustomPassword" class="form-label">Password</label>
              <input type="password" class="form-control" id="validationCustomPassword" name="password" placeholder="Enter your password" required>
              <div class="invalid-feedback">
                Please provide a valid password.
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Additional Information Card -->
      <div class="card mb-4">
        <div class="card-header">
          <h6>Additional Information</h6>
        </div>
        <div class="card-body">
          <div class="row g-3">
            <div class="col-md-6">
              <label for="validationCustomAge" class="form-label">Age</label>
              <input type="number" class="form-control" id="validationCustomAge" name="age" min="18" max="120">
              <div class="invalid-feedback">
                Please provide a valid age (18-120).
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustomDate" class="form-label">Birth Date</label>
              <input type="date" class="form-control" id="validationCustomDate" name="birthDate">
              <div class="invalid-feedback">
                Please select a valid date.
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustom03" class="form-label">City</label>
              <input type="text" class="form-control" id="validationCustom03" name="city">
              <div class="invalid-feedback">
                Please provide a valid city.
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustom04" class="form-label">State</label>
              <select class="form-select" id="validationCustom04" name="state" required>
                <option selected disabled value="">Choose...</option>
                <option value="CA">California</option>
                <option value="TX">Texas</option>
                <option value="NY">New York</option>
                <option value="FL">Florida</option>
                <option value="IL">Illinois</option>
                <option value="PA">Pennsylvania</option>
                <option value="OH">Ohio</option>
                <option value="GA">Georgia</option>
                <option value="NC">North Carolina</option>
                <option value="MI">Michigan</option>
              </select>
              <div class="invalid-feedback">
                Please select a valid state.
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustom05" class="form-label">Zip</label>
              <input type="text" class="form-control" id="validationCustom05" name="zip" required>
              <div class="invalid-feedback">
                Please provide a valid zip.
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Preferences Card -->
      <div class="card mb-4">
        <div class="card-header">
          <h6>Preferences</h6>
        </div>
        <div class="card-body">
          <div class="row g-3">
            <div class="col-12">
              <label class="form-label">Gender</label>
              <div class="row">
                <div class="col-md-4">
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="gender" id="genderMale" value="male" required>
                    <label class="form-check-label" for="genderMale">
                      Male
                    </label>
                    <div class="invalid-feedback">
                      Please select a gender.
                    </div>
                  </div>
                </div>
                <div class="col-md-4">
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="gender" id="genderFemale" value="female">
                    <label class="form-check-label" for="genderFemale">
                      Female
                    </label>
                  </div>
                </div>
                <div class="col-md-4">
                  <div class="form-check">
                    <input class="form-check-input" type="radio" name="gender" id="genderOther" value="other">
                    <label class="form-check-label" for="genderOther">
                      Other
                    </label>
                  </div>
                </div>
              </div>
            </div>
            <div class="col-12">
              <label for="validationCustomRange" class="form-label">Experience Level (0-10)</label>
              <div class="d-flex align-items-center gap-3">
                <input type="range" class="form-range flex-grow-1" id="validationCustomRange" name="experienceLevel" min="0" max="10" value="5">
                <div class="badge text-bg-primary fs-6 px-3 py-2" id="rangeValue">5</div>
              </div>
              <div class="invalid-feedback">
                Please select your experience level.
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustomColor" class="form-label">Favorite Color</label>
              <input type="color" class="form-control form-control-color" id="validationCustomColor" name="favoriteColor" value="#563d7c">
              <div class="invalid-feedback">
                Please select a color.
              </div>
            </div>
            <div class="col-md-6">
              <label class="form-label">Email Notifications</label>
              <div class="form-check form-switch">
                <input class="form-check-input" type="checkbox" role="switch" id="validationCustomSwitch" name="emailNotifications" checked>
                <label class="form-check-label" for="validationCustomSwitch">
                  Receive email notifications
                </label>
                <div class="invalid-feedback">
                  Please configure your notification preference.
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Media and Bio Cards in a row -->
      <div class="row">
        <div class="col-md-6">
          <!-- Media Card -->
          <div class="card mb-4">
            <div class="card-header">
              <h6>Media</h6>
            </div>
            <div class="card-body">
              <div class="row g-3">
                <div class="col-12">
                  <label for="validationCustomFile" class="form-label">Profile Picture</label>
                  <input type="file" class="form-control" id="validationCustomFile" name="profilePicture" accept="image/*">
                  <div class="invalid-feedback">
                    Please select a valid image file.
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="col-md-6">
          <!-- Bio Card -->
          <div class="card mb-4">
            <div class="card-header">
              <h6>About You</h6>
            </div>
            <div class="card-body">
              <div class="row g-3">
                <div class="col-12">
                  <label for="validationCustomBio" class="form-label">Bio</label>
                  <textarea class="form-control" id="validationCustomBio" name="bio" rows="3" placeholder="Tell us about yourself..."></textarea>
                  <div class="invalid-feedback">
                    Please provide a brief bio.
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- Special Fields Card -->
      <div class="card mb-4">
        <div class="card-header">
          <h6>Special Fields</h6>
        </div>
        <div class="card-body">
          <div class="row g-3">
            <div class="col-md-6">
              <label for="validationCustomDisabled" class="form-label">Disabled Field</label>
              <input type="text" class="form-control" id="validationCustomDisabled" value="This field is disabled" disabled>
              <div class="form-text">
                This field is disabled and cannot be edited.
              </div>
            </div>
            <div class="col-md-6">
              <label for="validationCustomReadonly" class="form-label">Readonly Field</label>
              <input type="text" class="form-control" id="validationCustomReadonly" value="This field is readonly" readonly>
              <div class="form-text">
                This field is readonly and cannot be edited.
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Right Column - Agreement and Submit -->
    <div class="col-lg-4">
      <div class="card">
        <div class="card-header">
          <h6>Review & Submit</h6>
        </div>
        <div class="card-body">
          <div class="d-flex flex-column gap-3">
            <div class="form-check">
              <input class="form-check-input" type="checkbox" value="" id="invalidCheck" name="agreeToTerms" required>
              <label class="form-check-label" for="invalidCheck">
                I agree to the terms and conditions
              </label>
              <div class="invalid-feedback">
                You must agree before submitting.
              </div>
            </div>

            <div class="d-grid gap-2">
              <button class="btn btn-primary btn-lg" type="submit">
                <i class="ri-send-plane-line me-2"></i>Submit Form
              </button>
            </div>

            <div class="text-muted small">
              <p>By submitting this form, you agree to our privacy policy and terms of service.</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>

</form>

<!-- Modal for displaying submitted data -->

<div class="modal fade" id="submissionModal" tabindex="-1" aria-labelledby="submissionModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="submissionModalLabel">Form Submission Summary</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <div id="submissionData"></div>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary" data-bs-dismiss="modal">OK</button>
      </div>
    </div>
  </div>
</div>
(() => {
  'use strict';

  // Add range slider feedback
  const rangeInput = document.getElementById('validationCustomRange');
  const rangeValue = document.getElementById('rangeValue');

  if (rangeInput && rangeValue) {
    // Set initial value
    rangeValue.textContent = rangeInput.value;

    // Update value on input change
    rangeInput.addEventListener('input', (e) => {
      rangeValue.textContent = e.target.value;
    });
  }

  // Fetch all the forms we want to apply custom Bootstrap validation styles to
  const forms = document.querySelectorAll('.needs-validation');

  // Loop over them and prevent submission
  Array.from(forms).forEach((form) => {
    form.addEventListener(
      'submit',
      (event) => {
        event.preventDefault();
        event.stopPropagation();

        if (!form.checkValidity()) {
          form.classList.add('was-validated');
          return;
        }

        form.classList.add('was-validated');

        // Collect form data
        const formData = new FormData(form);

        // Generate HTML for modal showing JSON representation
        let modalContent = '<div style="background: #f8f9fa; padding: 15px; border-radius: 5px; font-family: monospace; font-size: 13px;">';
        modalContent += '<strong>Form Data (JSON Representation):</strong><br><br>';

        // Show as JSON object
        const jsonData = {};
        for (let [key, value] of formData.entries()) {
          // Handle checkbox values
          if (key === 'agreeToTerms') {
            const checkbox = form.querySelector('#invalidCheck');
            jsonData[key] = checkbox.checked ? 'checked' : 'unchecked';
          }
          // Handle switch values
          else if (key === 'emailNotifications') {
            const switchInput = form.querySelector('#validationCustomSwitch');
            jsonData[key] = switchInput.checked ? 'enabled' : 'disabled';
          }
          // Handle file input
          else if (key === 'profilePicture') {
            const fileInput = form.querySelector('#validationCustomFile');
            if (fileInput && fileInput.files.length > 0) {
              jsonData[key] = fileInput.files[0].name;
            } else {
              jsonData[key] = '';
            }
          }
          else {
            jsonData[key] = value;
          }
        }

        // Ensure range input value is captured
        const rangeInput = form.querySelector('#validationCustomRange');
        if (rangeInput) {
          jsonData['experienceLevel'] = rangeInput.value;
        }

        modalContent += '<pre style="margin: 5px 0; white-space: pre-wrap; word-break: break-all; background: #ffffff; padding: 10px; border: 1px solid #dee2e6; border-radius: 3px;">' + JSON.stringify(jsonData, null, 2) + '</pre>';

        modalContent += '</div>';

        // Update modal content and show it
        document.getElementById('submissionData').innerHTML = modalContent;

        // Show the modal
        const modal = new bootstrap.Modal(document.getElementById('submissionModal'));
        modal.show();
      },
      false
    );
  });

})();
Quick Links
Admin
Admin Dashboard Example

Themes

Other