← Back to Blog

How to Add Form Validation to Your Tailwind CSS Contact Form

Surjith

In this tutorial, we will explore how to enhance your Tailwind CSS contact form with form validation using Peer & Invalid Sibling States. By implementing these techniques, you can ensure that users provide accurate information before submitting the form.

Adding form validation to your contact form is crucial for improving user experience and data accuracy. With Tailwind CSS and the Peer & Invalid Sibling States approach, you can create visually appealing and functional validation messages that guide users to correct any errors before submission.

Here's a live demo: Tailwind CSS Form Validation

Contact Form

Step 1: Set up the HTML Structure

Begin by creating your contact form with the necessary input fields. Make sure to include the required attribute for mandatory fields and assign unique name attributes to each field.

<form id="form" novalidate>
  <input type="text" name="name" required placeholder="Name*" />
  <input type="email" name="email" required placeholder="Email*" />
  <textarea name="message" required placeholder="Message*"></textarea>
  <button type="submit">Submit</button>
</form>

Step 2: Add Tailwind CSS Classes

Next, apply Tailwind CSS classes to each input. For sanity, we have removed other classes to focus on the validation part. Here's an example of how to style the input fields:

<div>
    <input
      type="text"
      name="FirstName"
      required
      class="peer border-2 focus:ring-2 [.validated_&]:invalid:border-pink-600 [.validated_&]:invalid:ring-2 [.validated_&]:invalid:ring-pink-200"
      placeholder="Name*"
    />
    <p class="mt-2 hidden [.validated_&]:peer-invalid:block text-pink-600">
        Please provide your first name.
    </p>
</div>

Here's what's happening in the code:

  • We've added the peer class to the input field and the sibling paragraph element to establish a peer relationship.
  • The border-2 class adds a border to the input field.
  • The focus:ring-2 class adds a ring on clicking the input field.
  • The [.validated_&]:invalid selector applies styles to the input field when it is invalid.
  • The .validated will be class name we will add to the <form> element when the form is submitted (using javascript). More on that later.
  • The border-pink-600 class changes the border color to pink when the parent has class .validate and the input field is invalid.
  • Now, the peer-invalid class will be added to the input field when it is invalid. This will make the sibling paragraph visible.

Here's another example for the email input field:

<div>
    <input
      type="email"
      name="email"
      required
      class="peer border-2 focus:ring-2 [.validated_&]:invalid:border-pink-600 [.validated_&]:invalid:ring-2 [.validated_&]:invalid:ring-pink-200"
      placeholder="Email*"
    />
    <p class="mt-2 hidden [.validated_&]:peer-invalid:block text-pink-600">
        Please provide your email.
    </p>
    <p class="mt-2 hidden [.validated_&]:peer-[:not(:placeholder-shown)]:peer-invalid:block text-pink-600">
        Please enter a valid email address.
    </p>
</div>

Here, we've added an additional paragraph element to display an error message when the email field is empty or contains an invalid email address. The [.validated_&]:peer-[:not(:placeholder-shown)]:peer-invalid selector applies styles to the input field when it is invalid and the placeholder is not shown.

Step 3: Implement JavaScript Validation

Finally, add JavaScript to validate the form fields and display error messages when necessary. Here's an example of how to achieve this:

<script>
  const form = document.getElementById("form");

  form.addEventListener("submit", function (e) {
    e.preventDefault();
    form.classList.add("validated");
    if (!form.checkValidity()) {
      form.querySelectorAll(":invalid")[0].focus();
      return;
    }
    // Submit the form
  });
</script>

Step 4: Test and Refine

Test your form by entering valid and invalid data. Ensure that the validation messages are displayed correctly and that the form submits successfully when all fields are valid. Refine the styles and validation messages as needed.

Here's the final code (with form submissions using Web3Forms API):

<!--
     // This is a working contact form.
     // You can get your access key from https://web3forms.com for free
-->
<form action="https://api.web3forms.com/submit" method="POST" id="form" novalidate>
  <!-- Add your access_key here -->
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY_HERE" />
  <input type="checkbox" class="hidden" style="display:none" name="botcheck" />

  <div class="grid md:grid-cols-2 gap-8 max-w-4xl mx-auto mt-14">
    <div>
      <input
      type="text"
      name="FirstName"
      required
      class="peer border-2 border-muted-medium py-4 px-4 rounded-xl placeholder:text-default focus:border-primary focus:ring-1 focus:ring-primary outline-none w-full [.validated_&]:invalid:border-pink-600 [.validated_&]:invalid:ring-2 [.validated_&]:invalid:ring-pink-200"
      placeholder="Name*"
    />
      <p class="mt-2 hidden [.validated_&]:peer-invalid:block text-pink-600 text-sm">
        Please provide your first name.
      </p>
    </div>
    <div>
      <input
      type="text"
      name="LastName"
      required
      class="peer border-2 border-muted-medium py-4 px-4 rounded-xl placeholder:text-default focus:border-primary focus:ring-1 focus:ring-primary outline-none w-full [.validated_&]:invalid:border-pink-600 [.validated_&]:invalid:ring-2 [.validated_&]:invalid:ring-pink-200"
      placeholder="Last name*"
    />
      <p class="mt-2 hidden [.validated_&]:peer-invalid:block text-pink-600 text-sm">
        Please provide your last name.
      </p>
    </div>
    <div>
      <input
      type="email"
      name="email"
      required
      class="peer border-2 border-muted-medium py-4 px-4 rounded-xl placeholder:text-default focus:border-primary focus:ring-1 focus:ring-primary outline-none w-full [.validated_&]:invalid:border-pink-600 [.validated_&]:invalid:ring-2 [.validated_&]:invalid:ring-pink-200"
      placeholder="Email address*"
    />
      <p class="mt-2 hidden [.validated_&]:peer-placeholder-shown:peer-invalid:block text-pink-600 text-sm">
        Please provide your email.
      </p>
      <p
        class="mt-2 hidden [.validated_&]:peer-[:not(:placeholder-shown)]:peer-invalid:block text-pink-600 text-sm">
        Please enter a valid email address.
      </p>
    </div>
    <div>
      <input
      type="text"
      name="phone"
      required
      class="peer border-2 border-muted-medium py-4 px-4 rounded-xl placeholder:text-default focus:border-primary focus:ring-1 focus:ring-primary outline-none w-full [.validated_&]:invalid:border-pink-600 [.validated_&]:invalid:ring-2 [.validated_&]:invalid:ring-pink-200"
      placeholder="Phone*"
    />
      <p class="mt-2 hidden [.validated_&]:peer-invalid:block text-pink-600 text-sm">
        Please provide your phone number.
      </p>
    </div>

    <div class="md:col-span-2">
      <textarea
      name="message"
      required
      class="peer border-2 border-muted-medium py-4 px-4 rounded-xl placeholder:text-default focus:border-primary focus:ring-1 focus:ring-primary outline-none w-full h-40 [.validated_&]:invalid:border-pink-600 [.validated_&]:invalid:ring-2 [.validated_&]:invalid:ring-pink-200"
      placeholder="Message*"></textarea>
      <p class="mt-2 hidden [.validated_&]:peer-invalid:block text-pink-600 text-sm">
        Please provide your message.
      </p>
    </div>
    <div class="md:col-span-2">
      <div class="text-center">
        <button
        type="submit"
        class="bg-pink-500 text-lg font-medium rounded-lg py-5 px-14 text-white transition hover:bg-primary-strong">
        Submit Now
      </button>

        <div id="result" class="mt-3 text-center"></div>
      </div>
    </div>
  </div>
</form>

<script>
	const form = document.getElementById("form");
  const result = document.getElementById("result");

  form.addEventListener("submit", function (e) {
    e.preventDefault();
    form.classList.add("validated");
    if (!form.checkValidity()) {
      form.querySelectorAll(":invalid")[0].focus();
      return;
    }
    const formData = new FormData(form);
    const object = Object.fromEntries(formData);
    const json = JSON.stringify(object);

    result.innerHTML = "Sending...";

    fetch("https://api.web3forms.com/submit", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: json,
    })
      .then(async (response) => {
        let json = await response.json();
        if (response.status == 200) {
          result.classList.add("text-green-500");
          result.innerHTML = json.message;
        } else {
          console.log(response);
          result.classList.add("text-red-500");
          result.innerHTML = json.message;
        }
      })
      .catch((error) => {
        console.log(error);
        result.innerHTML = "Something went wrong!";
      })
      .then(function () {
        form.reset();
        form.classList.remove("validated");
        setTimeout(() => {
          result.style.display = "none";
        }, 5000);
      });
  });
</script>

Conclusion

By following these steps, you can successfully add form validation to your Tailwind CSS contact form using Peer & Invalid Sibling States. This approach allows you to create visually appealing and functional validation messages that guide users to correct any errors before submission.

Get Started!

Create your contact form for static website in minutes.

Create your Access Key

No Signup required