Hello There,
TailwindCSS is a utility-first library which encourages to build everything using it's utility classes. With growing projects, we see the set of elements repeating themselves. With a utility-first approach, we not only repeat the list of classes but also the html structure of those elements. Best and well known way of handling this is creating components for mostly used elements.
In this article, I will be covering one mostly used component in any project - Form Input. Let’s dive in and understand how we can create it.
We will be creating an input element similar to the input shown in the image above. Let’s divide the whole process into 3 different parts -
- Styling the element.
- Handling error.
- Improving its usability.
1. Styling the element
If you notice, the input element has a label and border around both label and input. So let's take a wrapper div and put our label plus input element inside it. Hence the HTML look something like this -
const Input = (props) => {
const { id, placeholder = '', label = '', type = 'text', ...rest } = props;
return (
<div>
<label htmlFor={id}>{label}</label>
<input type={type} id={id} placeholder={placeholder} {...rest} />
</div>
);
};
export default Input;
This is the simplest react component which will take id
, label
, type
and placeholder
as a prop and we have also added ...rest
to maintain its flexibility.
Now, to add styling, the idea is, add a border to the outer div and place the label inside. Also, hide the border for the input element.
After adding Tailwind's utility classes this input will look like this -
const Input = (props) => {
const { id, placeholder = '', label = '', type = 'text', ...rest } = props;
return (
<div
className={`border transition duration-150 ease-in-out focus-within:border-primary border-gray-gray4`}
>
<label
htmlFor={id}
className={`text-xs text-primary font-light placeholder-gray-gray4 px-2 pt-1.5`}
>
{label}
</label>
<input
type={type}
className={`w-full px-2 pb-1.5 text-primary outline-none text-base font-light rounded-md`}
id={id}
placeholder={placeholder}
{...rest}
/>
</div>
);
};
export default Input;
Notice how we have used focus-within
property to change the border color after focusing input.
Till this point, we have already created a good looking input element. But, this element still have two issues -
- It will not show error
- If a user clicks on the box, outside the
<input/>
tag, the input will not be focused.
Let's solve these issues now.
2. Handling input error
To show input error efficiently, we will need to add two things, we will need to make the border red when error occurs and we will need to show error text below the input component.
Let's see the code first -
const Input = (props) => {
const {
id,
wrapperClassName = '',
placeholder = '',
label = '',
type = 'text',
error = '',
required = false,
...rest
} = props;
return (
<div className={wrapperClassName}>
<div
className={`border transition duration-150 ease-in-out ${
error
? 'focus-within:border-red border-red'
: 'focus-within:border-primary border-gray-gray4'
}`}
>
<label
htmlFor={id}
className='text-xs text-primary font-light placeholder-gray-gray4 px-2 pt-1.5'
>
{label} {required && <span className='text-red'>*</span>}
</label>
<input
type={type}
className='w-full px-2 pb-1.5 text-primary outline-none text-base font-light rounded-md'
id={id}
placeholder={placeholder}
{...rest}
/>
</div>
{error && <p className='text-xs pl-2 text-red mb-4'>{error}</p>}
</div>
);
};
export default Input;
Here, to add the error, we added a <p>
tag in the bottom. As React needs only a single element wrapper, we added one more div
outside. This div will be helpful to add margins or other styles to complete the input component.
We have also changed the border color conditionally for the outer component and added an asterisk, if the input is mandatory.
With this error handling, we have almost finished creating out component. One last thing pending here is, focusing our input when we click the outer div.
3. Improving usability
To focus our input on after clicking the outside div, we have useRef
to our rescue😅. We added a ref to the input element inside our component. When we will click the outside div, we will add focus to the input using this ref.
Notice the onClick
event we added to the outside div of input. This will solve all our requirements and a complete input component becomes ready.
Final version of our component will look something like this -
import { useRef } from 'react';
const Input = (props) => {
const {
id,
wrapperClassName = '',
placeholder = '',
label = '',
type = 'text',
error = false,
errorText = '',
required = false,
...rest
} = props;
const inputRef = useRef();
return (
<div className={wrapperClassName}>
<div
className={`border transition duration-150 ease-in-out ${
error
? 'focus-within:border-red border-red'
: 'focus-within:border-primary border-gray-gray4'
}`}
onClick={() => inputRef.current.focus()}
>
<label
htmlFor={id}
className='text-xs text-primary font-light placeholder-gray-gray4 px-2 pt-1.5'
>
{label} {required && <span className='text-red'>*</span>}
</label>
<input
ref={inputRef}
type={type}
className='w-full px-2 pb-1.5 text-primary outline-none text-base font-light rounded-md'
id={id}
placeholder={placeholder}
{...rest}
/>
</div>
{errorText && (
<p className='text-xs pl-2 text-red mb-4'>{errorText}</p>
)}
</div>
);
};
export default Input;
And that's it!
You can create many such components using Tailwind and React. I have used this code for my side project and I am creating more such components in my github repository.
Thank you so much for reading this article and always happy to receive your feedback. You can also connect with me on Twitter or buy me a coffee if you like my articles.
Thanks a lot! Keep learning 🙌
Top comments (3)
Nice article! Sweetly explained. 💯
Something positive after seeing Tailwind getting bashed with React it really looks cool same for Vue. Thanks nice article
Even I was fedup of reading Tailwind hate!
Its just a framework/tool, like it use it, don't like it move on 😅
And Thank you 😇