<div id="app">
	<div class="h-2 w-100 bg-blue-500"></div>
	<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 -->

		<!-- 		examples without borders -->
		<div class="border-l-4 border-blue-500 p-3 rounded mb-6 mt-10 shadow bg-gray-100 font-semibold text-gray-600 tracking-wide text-lg">
			Input components
			without borders
		</div>
		<text-input label="Email" class="mb-4" :bordered="false">
		</text-input>
		<text-input label="Password" type="password" class="mb-4" :bordered="false"></text-input>
		<!-- 		examples without borders ended -->

	</div>

</div>
const TextInput = {
	name: "TextInput",
	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>`,

	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
			};
		}
	}
};

new Vue({
	el: "#app",
	components: {
		TextInput
	},
	data: {
		errorEmail: "",
		errors: {
			errorEmail: ["The email field is required."],
			errorPassword: ["The password confirmation does not match."]
		}
	}
});
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/1.2.0/tailwind.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js