DEV Community

loading...

Reuse component functionality with the Vue wrapper component pattern

Matthew Blewitt
Javascript Developer, currently working in a Nuxt/Vue stack
・2 min read

When building Vue components I often need to create a component that has similar functionality to an existing base component. I want to re-use the base component and extend it to create a new one. A good pattern for this is the wrapper component.

Think of a BaseButton.vue. It takes some props and emits some events:

<template>
  <button
    :class="`btn btn-${theme}`"
    :disabled="disabled"
    @click="handleClick"
  >
    <slot />
  </button>
</template>

<script>
export default {
  name: "BaseButton",
  props: {
    theme: {
      type: String,
      default: "info",
      validator: val =>
        [
          "info",
          "success",
          "error"
        ].indexOf(val) !== -1
    },
    disabled: {
      type: Boolean,
      default: false
    }
  }
  methods: {
    handleClick() {
      this.$emit("click");
    }
  }
};
</script>

<style scoped>
.btn {
  outline: none;
  border: none;
  color: white;
}
.btn-info {
  background: blue;
}
.btn-success {
  background: green;
}
.btn-error {
  background: red;
}
</style>

I now have a need to build a button that shows a loading state with some text and disables the button when passed a prop of loading. I could cram this new functionality into the BaseButton, but a much cleaner solution is to use a wrapper component pattern. Let's create a LoadingButton.vue.

<template>
  <BaseButton v-bind="{ ...$props, ...$attrs }" v-on="$listeners">
    <template v-if="loading">Loading...</template>
    <slot v-else />
  </BaseButton>
</template>

<script>
import BaseButton from "~/components/BaseButton";
export default {
  components: {
    BaseButon
  },
  props: {
    ...BaseButton.props,
    loading: {
      type: Boolean,
      default: false
    }
  }
};
</script>

A couple of this are happening here:

  • Pass all the components instance details down to the base components via $props, $attr, $listeners. See more Vue in the docs.
  • Use BaseButton.props to maintain the same props passed down but add additional loading prop to create the new functionality.

A great little pattern for component reusability 🔥

Originally posted on https://matthewblewitt.com/posts/vue-wrapper-component

Discussion (0)