Creating a Button component using Vue and Tailwindcss
Tags: vue, tailwindcss, button, component
Published: 2020-03-14 17:32:34

> Today we are going to build a custom button component using Vue and Tailwindcss. Here you can look into my codepen.

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

As you have seen, I have kept different options for the colors, size, and roundness of the button. By default this button ships with a loading spinner. You could use the loading feature when you are waiting for the promise to return from axios.

It follows three steps -

- [Creating the component](#creating-component)
- [Using the component](#using-component)
- [Using the props](#using-prop)

<a name="creating-component"></a>

### Creating the Button Component

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

```js
//LoadingButton.vue
<template>
	<component
		:is="tag"
		:type="type"
		v-on="$listeners"
		:disabled="disableButton"
		:class="[btnClass, colorVariants]"
		:variant="variant"
		:variant-type="variantType"
		:size="size"
		:href="to"
	>
		<slot />
	</component>
</template>

<script>
export default {
	name: "LoadingButton",
	props: {
		tag: {
			type: String,
			default: "button"
		},
		disabled: {
			type: Boolean,
			default: false
		},
		variant: {
			type: String,
			default: "primary"
		},
		variantType: {
			type: String,
			default: "normal"
		},
		size: {
			type: String,
			default: "normal"
		},
		rounded: {
			type: String,
			default: "medium"
		},
		type: {
			type: String,
			default: ""
		},
		to: {
			type: String
		}
	},
	data() {
		return {
			loading: false,
			disableButton: this.disabled
		};
	},

	methods: {
		startLoading() {
			this.loading = true;
			this.disableButton = true;
		},
		stopLoading() {
			this.loading = false;
			this.disableButton = false;
		}
	},

	computed: {
		btnClass() {
			return {
				"base-spinner": this.loading == true,
				"cursor-not-allowed": this.disableButton == true,
				"base-button inline-flex align-middle align-items-center justify-center font-medium focus:outline-none border-2": true,

				"rounded-lg": this.rounded === "medium",
				"rounded-full": this.rounded === "large",

				"px-6 py-3": this.size == "normal",
				"px-4 py-2": this.size == "small"
			};
		},

		colorVariants() {
			switch (this.variant) {
				case "primary":
					switch (this.variantType) {
						case "normal":
							switch (this.disableButton) {
								case true:
									return "border-blue-300 bg-blue-300 text-white";
									break;
								default:
									break;
							}
							return "border-blue-600 bg-blue-600 hover:bg-blue-700 hover:border-blue-700 text-white";
							break;
						case "outline":
							return "border-gray-200 text-blue-500 hover:text-blue-700";
							break;
						default:
							break;
					}
					break;
				case "danger":
					switch (this.variantType) {
						case "normal":
							switch (this.disableButton) {
								case true:
									return "border-red-300 bg-red-300 text-white";
									break;
								default:
									break;
							}
							return "border-red-600 bg-red-600 hover:bg-red-700 hover:border-red-700 text-white";
							break;
						case "outline":
							return "border-gray-200 text-red-500 hover:text-red-600";
							break;
						default:
							break;
					}
					break;

				case "warning":
					switch (this.variantType) {
						case "normal":
							return "border-orange-600 bg-orange-600 hover:bg-orange-700 hover:border-orange-700 text-white";
							break;
						default:
							break;
					}
					break;

				case "success":
					switch (this.variantType) {
						case "normal":
							switch (this.disableButton) {
								case true:
									return "border-green-300 bg-green-300 text-white";
									break;
								default:
									break;
							}
							return "border-green-600 bg-green-600 hover:bg-green-700 hover:border-green-700 text-white";
							break;
						case "outline":
							return "border-2 border-gray-200 text-green-500 hover:text-green-700";
							break;
						default:
							break;
					}
					break;

				case "white":
					switch (this.variantType) {
						case "normal":
							return "border-white bg-white text-blue-600 hover:text-blue-800";
							break;
						default:
							break;
					}
					break;

				case "secondary":
					switch (this.variantType) {
						case "outline":
							return "border-gray-300 text-gray-500 hover:text-blue-500";
							break;
						default:
							break;
					}
					break;

				default:
					break;
			}
		}
	}
};
</script>

```

<a name="using-component"></a>

### Using the component

Now, we will use the `loading-button` component in our `App.vue`.

```js
// App.vue
<template>
  <div id="app">
    <loading-button @click="submit" ref="submitButton">Save Changes</loading-button>
  </div>
</template>

<script>
import LoadingButton from "@/Shared/LoadingButton";

export default {
  name: "app",

  components: {
    LoadingButton
  },

  methods: {
      submit() {
          //calling the startLoading method of the button, which enables the button to loading state
          this.$refs.submitButton.startLoading();
      }
  }
};
</script>
```

<a name="using-prop"></a>

### Using the props

`default option`

| Name         | Type   | Description                   | Accepted                                       |
| :----------- | ------ | :---------------------------- | :--------------------------------------------- |
| variant      | String | Color scheme of the button    | `primary`, danger, success, warning, secondary |
| variant-type | String | Normal button or ghost button | `normal`, outline                              |
| rounded      | String | border radius of the button   | `medium`, large                                |
| size         | String | padding of the button         | `normal`, small                                |

<a name="methods-available"></a>

### Methods Available

`default option`

| Name         | Description                       |
| :----------- | :-------------------------------- |
| startLoading | start loading state of the button |
| stopLoading  | stop loading state of the button  |

#### N.B.

- Only <strong>primary</strong>, <strong>danger</strong> and <strong>success</strong> variant accepts a <strong>disabled</strong> prop.
- <strong>secondary variant</strong> is only available for outline button.