Creating an Input component using Vue and Tailwindcss
Tags: vue, tailwindcss, input, component
Published: 2020-03-08 17:52:23

> In this article we will be building an input component using Vue and Tailwindcss.

It consists of 3 sections - 
- [The Input Component](#the-input-component)
- [Using the Component](#using-the-component)
- [Props](#props)


## 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.

{% codepen https://codepen.io/abhisheksarmah/pen/OJVOayz %}

<a name="the-input-component"></a>
### The Input Component

Create a file called `TextInput.vue` in `/src/Shared/TextInput.vue` and add

```js
// 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>

```

<a name="using-the-component"></a>
### Using the component

Create a file called `ExampleComponent.vue` in `/src/components/ExampleComponent.vue` and add

```vue
<!-- 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>
```

<a name="props"></a>
### Props

`default option`

| Name     | Type    | Description                                | Accepted                                                                                      |
| :------- | ------- | :----------------------------------------- | :-------------------------------------------------------------------------------------------- |
| type     | String  | Input types                                | `text`, [default html5 input types](https://www.w3schools.com/html/html_form_input_types.asp) |
| 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 :)