In this article we will be building an input component using Vue and Tailwindcss.
It consists of 3 sections -
Getting Started
Have a look at the component how it will look like. I have added different options for adding label
, icons
, error messages
etc. to the component.
The Input Component
Create a file called TextInput.vue
in /src/Shared/TextInput.vue
and add
// TextInput.vue
<template>
<div>
<label
v-if="label"
class="form-label block mb-1 font-semibold text-gray-700"
:for="id"
>{{ label }}</label
>
<div class="relative">
<input
:id="id"
ref="input"
v-bind="$attrs"
class="px-2 py-2 h-12 leading-normal block w-full text-gray-800 bg-white font-sans rounded-lg text-left appearance-none outline-none"
:class="[
{
'border-red-400': errors.length,
'pl-12': withIcon === true
},
classes
]"
:type="type"
:value="value"
@input="$emit('input', $event.target.value)"
@keydown="$emit('keydown', $event)"
@blur="$emit('blur', $event)"
@keyup="$emit('keyup', $event)"
/>
<div v-if="errors.length" class="text-red-600 mt-1 text-sm">
{{ errors[0] }}
</div>
<svg
class="absolute text-red-600 fill-current"
style="top: 12px; right: 12px"
v-if="errors.length"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path
d="M11.953,2C6.465,2,2,6.486,2,12s4.486,10,10,10s10-4.486,10-10S17.493,2,11.953,2z M13,17h-2v-2h2V17z M13,13h-2V7h2V13z"
/>
</svg>
<div
class="absolute left-0 top-0 bottom-0 w-10 block ml-2"
v-if="withIcon"
>
<slot name="icon"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TextInput',
inheritAttrs: false,
props: {
id: {
type: String,
default() {
return `text-input-${this._uid}`;
}
},
type: {
type: String,
default: "text"
},
value: String,
label: String,
errors: {
type: Array,
default: () => []
},
withIcon: {
type: Boolean,
default: false
},
bordered: {
type: Boolean,
default: true
}
},
methods: {
focus() {
this.$refs.input.focus();
},
select() {
this.$refs.input.select();
},
setSelectionRange(start, end) {
this.$refs.input.setSelectionRange(start, end);
}
},
computed: {
classes() {
return {
"border-2 focus:border-blue-600 focus:border-blue-600":
this.bordered === true,
"border bg-gray-200 focus:bg-white": this.bordered === false
};
}
}
};
</script>
Using the component
Create a file called ExampleComponent.vue
in /src/components/ExampleComponent.vue
and add
<!-- ExampleComponent.vue -->
<template>
<div class="max-w-lg mx-auto my-12">
<!-- examples with lables -->
<div
class="border-l-4 border-blue-500 p-3 rounded mb-6 shadow bg-gray-100 font-semibold text-gray-600 tracking-wide text-lg"
>
Input components with labels
</div>
<text-input label="Email" class="mb-4"></text-input>
<text-input label="Password" type="password" class="mb-4"></text-input>
<!-- examples with label ended -->
<!-- examples with icons -->
<div
class="border-l-4 border-blue-500 p-3 rounded my-10 mb-6 shadow bg-gray-100 font-semibold text-gray-600 tracking-wide text-lg"
>
Input components with Icons
</div>
<text-input class="mb-4" with-icon placeholder="Enter your email...">
<template #icon>
<svg
class="mt-2 w-8 h-8 stroke-current text-gray-100"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
>
<path
d="M3 8L10.8906 13.2604C11.5624 13.7083 12.4376 13.7083 13.1094 13.2604L21 8M5 19H19C20.1046 19 21 18.1046 21 17V7C21 5.89543 20.1046 5 19 5H5C3.89543 5 3 5.89543 3 7V17C3 18.1046 3.89543 19 5 19Z"
stroke="#4A5568"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
</text-input>
<text-input
class="mb-4"
type="password"
with-icon
placeholder="Enter your password..."
>
<template #icon>
<svg
class="mt-2 w-8 h-8 stroke-current text-gray-100"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
>
<path
d="M12 15V17M6 21H18C19.1046 21 20 20.1046 20 19V13C20 11.8954 19.1046 11 18 11H6C4.89543 11 4 11.8954 4 13V19C4 20.1046 4.89543 21 6 21ZM16 11V7C16 4.79086 14.2091 3 12 3C9.79086 3 8 4.79086 8 7V11H16Z"
stroke="#4A5568"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</template>
</text-input>
<!-- examples with icons ended -->
<!-- examples with error messages -->
<div
class="border-l-4 border-red-500 p-3 rounded mb-6 mt-10 shadow bg-gray-100 font-semibold text-gray-600 tracking-wide text-lg"
>
Input components with Error Messages
</div>
<text-input
label="Email"
class="mb-4"
:errors="errors['errorEmail']"
@keydown="delete errors['errorEmail']"
>
</text-input>
<text-input
label="Password"
type="password"
class="mb-4"
:errors="errors['errorPassword']"
@keydown="delete errors['errorPassword']"
></text-input>
<!-- examples with error messages ended -->
</div>
</template>
<script>
import TextInput from "@/Shared/TextInput";
export default {
components: {
TextInput
},
data() {
return {
errorEmail: "",
errors: {
errorEmail: ["The email field is required."],
errorPassword: ["The password confirmation does not match."]
}
};
}
};
</script>
Props
default option
Name | Type | Description | Accepted |
---|---|---|---|
type | String | Input types |
text , default html5 input types
|
label | String | Label of the input | |
withIcon | Boolean | if set, icon is shown | true, false
|
errors | Array | Error that need to be shown with the input | |
bordered | Boolean | input has border or not |
true , false |
Happy Coding :)
Top comments (1)
thanks, that was a good guid for me to make form components