Create multipurpose buttons with Nuxt & Tailwind

UI
Tailwind
Nuxt
August 28, 2021

I'll show you the concept of creating a reusable button component for your next Nuxt project with Tailwind CSS in just a few steps.

Buttons with Nuxt & Tailwind CSS

⚠️

This post assumes that you have already integrated Tailwind CSS into your project. If not, see the tutorial here.

Goals to achieve

  • One component should cover all necessary scenarios for its purpose
  • Single file maintenance
  • Intuitive development

How to

First, we create a new component with the name B-Btn.vue. The "B" should stand for "Base" and symbolizes a base component that can be reused. This should help us to identify these kind of components later in the template and also simplify the implementation by using short and understandable names.

Vue
B-Btn.vue
<template>
  <button @click="$emit('click')">
    <slot></slot>
  </button>
</template>

Make it multipurpose

First, we can implement the possibility to use the button as a link. For this, we use dynamic components. With the to prop we want the component to become <nuxt-link> and with href a <a> tag.

Vue
B-Btn.vue
<template>
  <component
    :is="to ? 'nuxt-link' : href ? 'a' : 'button'"
    :to="to"
    :href="href"
    @click="$emit('click')"
  >
    <slot></slot>
  </component>
</template>

<script>
  export default {
    props: {
      to: {
        type: String,
        default: null
      },
      href: {
        type: String,
        default: null
      }
    }
  };
</script> 

Now the default is a button that can be controlled with the @click event. If you specify the to or href attribute, the button will automatically be a link.

Vue
// Button
<B-Btn @click="onClickButton">Button</B-Btn>

// Button as nuxt-link
<B-Btn
  to="/en/blog"
  rel="next"
>Link</B-Btn>

// Button as anchor
<B-Btn 
  href="https://thenextbit.de/en/blog"
  target="_blank"
  rel="external"
>Link</B-Btn>

Styling

You should add your base classes for the style, according to your corporate design. All classes that should contribute to changes in color, shape or variant we control via the props and dynamic class bindings.

Colors

In the following example, we will control the color of the button through the prop color. We define in the computed properties the colors we need. In this case I have created primary, success and error.

Then we can dynamically bind the classes in the template and give the button a different look depending on the color prop.

Vue
B-Btn.vue
<template>
  <component
    :class="[
      { 'bg-indigo-700': primary },
      { 'bg-green-500': success },
      { 'bg-red-600': error }
    ]"
  >
    <slot></slot>
  </component>
</template>

<script>
  export default {
    props: {
      color: {
        type: String,
        default: 'primary'
      }
    },

    computed: {
      primary() {
        return this.color === 'primary';
      },
      success() {
        return this.color === 'success';
      },
      error() {
        return this.color === 'error';
      }
    }
  };
</script>

Sizes

Similar to the last examples, we can now control sizes and variants through props.

Set the props according to your requirements and bind a class to the respective state. It is recommended to always have a base state that is already active without a prop. An example here could be four different sizes like sm, a default size, lg and xl:

Vue
B-Btn.vue
<script>
  export default {
    props: {
      sm: {
        type: Boolean,
        default: false
      },
      lg: {
        type: Boolean,
        default: false
      },
      xl: {
        type: Boolean,
        default: false
      }
    }
  };
</script>

Use Slots

You can make your button even more dynamic by adding more slots. An example can be that you want to insert an icon before or after your buttons text.

The following representation is only a simplification. You can of course have wrapper elements with custom styles for the respective slots. But this is to be implemented according to your requirements.

Vue
B-Btn.vue
<template>
  <component 
    class="flex items-center space-x-2"
  >
    <slot name="prepend"></slot>
    <slot name="default"></slot>
    <slot name="append"></slot>
  </component>
</template>

And in use, it can look like this:

Vue
<B-Btn
  color="success"
  to="/contact"
>
  <MailIcon slot="prepend" />
  <span slot="default">contact us</span>
</B-Btn>

More to know

If the <button> tag is the root element in your <B-Btn> component, then the attributes like rel, target and title, which are important for SEO when using links, will automatically apply to the button element. So you don't need to predefine everything in the component props.

You can (and probably should) also dynamically bind and style the disabled and loading states.

The complexity and sophistication of the component will of course depend on your design requirements. Since this post is just to show the rough concepts, this will do for now.

Conclusion

I hope I was able to introduce you to the concepts of how to build reusable and flexible components with Nuxt.js and Tailwind CSS.