Create multipurpose buttons with Nuxt & Tailwind
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.
This post assumes that you have already integrated Tailwind CSS into your project. If not, look into the official tutorial.
Goals to achieve
One component should cover all necessary scenarios for its purpose
Single file maintenance
Intuitive development
Basis
First, we create a new component with the name BaseButton.vue
. The "Base" symbolizes a reusable base component. This should help us to identify these kinds of components later in the template and also simplify the implementation by using short and understandable names.
<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.
<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.
// Button
<BaseButton
@click="onClickButton"
>Button</BaseButton>
// Button as nuxt-link
<BaseButton
to="/en/blog"
rel="next"
>Link</BaseButton>
// Button as anchor
<BaseButton
href="https://thenextbit.de/en/blog"
target="_blank"
rel="external"
>Link</BaseButton>
Styles
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.
In the following example, we will control the color of the button through the prop color
. We can define the colors we need with computed properties. 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.
<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
:
<script>
export default {
props: {
sm: {
type: Boolean,
default: false
},
lg: {
type: Boolean,
default: false
},
xl: {
type: Boolean,
default: false
}
}
};
</script>
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.
<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:
<BaseButton
color="success"
to="/contact"
>
<MailIcon slot="prepend" />
<span slot="default">contact us</span>
</BaseButton>
More to know
If the <button>
tag is the root element in your <BaseButton>
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.