Introduction
In this article, we will look at how to add custom validation to a form with TailwindCSS. The form is a simple HTML form and can be used on any project that uses TailwindCSS.
Github
Check out the complete source code here
Prerequisites
We are required to have TailwindCSS set up in an existing project. If that's not the case, follow this guide to set one up like it was done for this article._
Basic Form Structure
The form is a simple login form with two input fields and a submit button with added styles from TailwindCSS.
<main class="min-h-screen bg-blue-100 flex items-center justify-center text-gray-500 text-sm">
<form
class="bg-white shadow-lg rounded-md p-5 md:p-10 flex flex-col w-11/12 max-w-lg"
>
<label for="email" class="mb-5">
<span>Email</span>
<input
type="email"
name="email"
id="email"
class="w-full rounded border border-gray-300 bg-inherit p-3 shadow shadow-gray-100 mt-2 appearance-none outline-none text-neutral-800"
placeholder=" "
required
/>
</label>
<label for="password" class="mb-5">
<span>Password</span>
<input
type="password"
name="password"
id="password"
class="w-full rounded border border-gray-300 bg-inherit p-3 shadow shadow-gray-100 mt-2 appearance-none outline-none text-neutral-800"
placeholder=" "
required
/>
</label>
<button type="submit" class="mt-5 bg-blue-500 py-3 rounded-md text-white">Submit</button>
</form>
</main>
Our form has two required input fields, and a submit button. Whenever we add a required attribute to input fields, it shows an error like the one below when we try to submit it without completing the fields.
This popup message is not very appealing, varies from browser to browser, and cannot be styled. We will look at how to customize this message to suit our needs.
Disabling the Default Validation Message
We will use the novalidate
attribute on the form to disable the default validation message. This attribute will disable the default validation message and allow us to add our own custom validation message.
<!-- ... -->
<form
class="bg-white shadow-lg rounded-md p-5 md:p-10 flex flex-col w-11/12 max-w-lg"
novalidate
>
<!-- ... -->
Adding Custom Validation to Input Fields
In CSS, we can use the :invalid
pseudo-class to style an input field when it is invalid. Tailwind CSS also makes it available to us. When it is invalid, we will add a red border to the input field. We can also add a regex pattern to the input field to force a certain style.
<!-- ... -->
<label for="email" class="mb-5">
<span>Email</span>
<input
type="email"
name="email"
id="email"
class="... invalid:[&:not(:placeholder-shown):not(:focus)]:border-red-500"
placeholder=" "
required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
/>
</label>
<label for="password" class="mb-5">
<span>Password</span>
<input
type="password"
name="password"
id="password"
class="... invalid:[&:not(:placeholder-shown):not(:focus)]:border-red-500"
placeholder=" "
required
pattern=".{7,}"
/>
</label>
<!-- ... -->
We use a standard regex pattern for the email field that checks for a valid email address.
For the password field, we use a regex pattern that checks for a password with a minimum of 7 characters.
We chained some tailwind classes to the invalid
class to style the input field when it is invalid. Let's break this down.
- We are using Arbitrary variants to add custom modifiers to the
invalid
class. -
:not(:placeholder-shown)
- This will ensure that the input field is not empty. We don't want to show the red border if the input field is empty. Hence, why we are using a space in the placeholder. We can decide to use text like "Enter your email" in the placeholder. -
:not(:focus)
- This will ensure that the input field is not focused. We don't want to show the red border if the input field is focused (i.e., the user is currently typing).
Adding a Custom Validation Message
We will use the peer
class to add a custom validation message. The peer
class allows us to style an element based on the state of another element. In this case, we will be styling the <span>
element based on the state of the input field.
<!-- ... -->
<label for="email" class="mb-5">
<span>Email</span>
<input
type="email"
name="email"
id="email"
class="... peer"
placeholder=" "
required
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$"
/>
<span class="mt-2 hidden text-sm text-red-500 peer-[&:not(:placeholder-shown):not(:focus):invalid]:block">
Please enter a valid email address
</span>
</label>
<!-- ... -->
We added the peer
class to the input field and then chained the peer
class to the arbitrary variants we used for the input field to change the <span>
elements' style from display: none
to display: block
when the input field is invalid.
Now, we've added custom validation styles and messages to our form. But we can still submit the form without filling in the fields or when they're invalid. We will be looking at how to prevent the form from being submitted when the fields are empty.
Preventing Form Submission
We will use the group
class on the form to prevent the form from being submitted when the fields are empty or invalid. Like the peer
class, the group
class allows us to style an element based on the state of the parent element.
Since the input fields are nested inside the form, the form will be invalid when any of the input fields are invalid. We can use this to disable the button and prevent the form from being submitted.
<!-- ... -->
<form
class="... group"
novalidate
>
<!-- ... -->
<button type="submit" class="... group-invalid:pointer-events-none group-invalid:opacity-30">Submit</button>
</form>
<!-- ... -->
We use the :invalid
pseudo-class to style the button when the form is invalid. We also chained the group-invalid
class to the pointer-events-none
and opacity-50
classes to disable the button and make it more transparent when the form is invalid.
Finished Look
Conclusion
TailwindCSS makes adding custom validation styles and messages to our forms easy without JavaScript. We can also use JavaScript to add more functionality to our forms. I hope we enjoyed reading this article. Feel free to ask questions in the comments section below.
Top comments (5)
Nice one. This is actually really nice to use when working with some static html site generator or framework like Astro! 🚀
I agree!
That's a great visual look for sure - however, it doesn't prevent submission through "enter" from one of the input fields - which is kindof a bummer.
I assume this is due to the "novalidate" on the form - though it'd be nice to know if there's a workaround for this.
Very useful post, thank you ..
Nice !!