DEV Community

Abhishek
Abhishek

Posted on • Edited on

Creating an Input component using Vue and Tailwindcss

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)

Collapse
 
profnami profile image
Nader Minaie

thanks, that was a good guid for me to make form components