Easily Handle Component Versioning with Vue3
Tags: vue, tutorial, coding, development
Published: 2021-04-29 15:09:18

---
title: Easily Handle Component Versioning with Vue3
published: true
description: Components refactoring can become a huge issue with breaking changes that can take hours to cleanup in a codebase. Here's an easy strategy to deal with Component Versioning.
tags: vue,tutorial,coding,development
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1g5dg9yajd4m09t7taj.jpg
---

VueJs is known for its simplicity and low learning curve, helping launch apps from beginners to senior devs alike.

But anyone who's spent time building up a codebase in Vue has learned with growth comes pain. Because of that it's important to address those scalable issues early on before an organization is stuck in a quagmire of tech debt and spaghetti code that can take days, weeks, and even months to correct.

Versioning components is one of those issues that can rub against a developers ego, but to care for "6 months in the future you", versioning components is an incredibly important time and energy saving strategy.

Tools like [bit.dev](https://bit.dev/) handle this issue very well, but I'm preferential to duck tape and toothpick homegrown solutions that work just as well as a service that can cost upwards of $200 per month.

### Why Do I Need To Version Components

If you're asking this question then you haven't had to deal with a design team that gets a new lead.

If you're asking this question then you haven't found a new library that more efficiently handles an issue that had been buggy since it's inception.

If you're asking this question then you haven't attended a Vue Conference and walked away thinking "duh, why haven't I always done it that way?"

In other words, your code _will_ change, and in Vue if it's a component that's implemented in a hundred different files, then you will be kicking yourself as you `ctrl+shift+F` your way through your codebase.

### Standard Component Usage

For this example, we'll take a simple Vue Button Component:

```vue
<template>
  <button
     :class="['button', { block, color }]"
     @click="$emit('click')">
     <slot />
  </button>
</template>

<script>
import { defineComponent } from '@vue/composition-api'

export default defineComponent({
  name: 'Button',
  props: {
    block: Boolean,
    color: {
      type: String,
      default: 'primary'
  },

  setup(props) {
    const colors = {
      primary: 'green',
      error: 'red',
      secondary: 'purple'
    }

    return {
      color: `style-${colors[props.color] || 'green'}`
    }
  }
})  
```

Where things get tricky is if you decide to take a new approach to how you want colors to set. Rather than using a named color table it'll instead act as a pass through style.

```vue
<template>
  <button
     :class="['button', { block }]"
     :style="buttonStyle"
     @click="$emit('click')">
     <slot />
  </button>
</template>

<script>
  [...]
  props: {
    color: {
      type: String,
      default: 'gray'
  },

  setup(props) {
    return {
      buttonStyle: computed(() => { color: props.color })
    }
  }
}
```

This will, of course, break any instance in which you had used the Button component.

### Handling Component Versions

Approaching this problem, the most straightforward solution is to create a stopgap between the code of the component, and how the component is called.

In this mindset then, we'll create a __shell__ component that'll wrap around __versioned__ components.

Most likely you're used to organizing your components as such:

```
src/
   components/
      VButton.vue
```

Which is probably useful in almost every scenario, but if you've happened to come across [Vue - The Road to Enterprise by Thomas Findlay](https://theroadtoenterprise.com/) (which I highly recommend if you're beginning to architect large scale Vue apps), then you'll know that organizing Vue components is vital for a digestible code base.

Borrowing a few concepts from Thomas, this is a good organizational strategy to handle component versioning:

```
src/
   components/
      global/
         VButton/
            index.vue   <-- shell
            VButton-v1.vue   <-- versioned
```

This will help keep your components nice and tidy, and with folders collapsed, the various component folders will provide easy reference for the grouping of shell and versioned components inside.

#### Writing a Shell Component

For the sake of this Button component, and most likely all simple components, there's going to be 4 main things we have to handle when building a shell:

1. Passing props
2. Passing attrs
3. Carrying emits
4. Passing slots

But first is how to handle the loading of the versioned component file:

```vue
<template>
  <component :is="buttonComponent">
    Button
  </component>
</template>

<script>
import { defineAsyncComponent, defineComponent } from '@nuxtjs/composition-api'

export default defineComponent({
  name: 'VButton',
  props: {
    version: {
      type: String,
      default: 'v1'
    },
  },

  setup(props) {
    const versionComponent = (version) => defineAsyncComponent(() => {
      return import(`./VButton-${version}.vue`)
    })

    return {
      buttonComponent: ref(versionComponent(props.version)),
    }
  }
})
</script>
```

Thanks to old tried and true `<component>` paired with Vue3's `defineAsyncComponent` this was actually a fairly easy lift.

Next is handling props, attrs, and emits:

```vue
<template>
  <component
    v-bind="{ ...$attrs, ...$props }"
    :is="nButtonComponent"
    @click="$emit('click')">
    Button
  </component>
</template>
```

Using built-in elements `$attrs` and `$props`, attrs and props are very easily passed to a child component to be digested.

And lastly, slots:

```vue
<template>
  <component
    v-bind="{ ...$attrs, ...$props }"
    :is="nButtonComponent"
    @click="$emit('click')">
    <slot
      v-for="(_, name) in $slots"
      :name="name"
      :slot="name" />
  </component>
</template>
```

The one flaw with using `$slots` is that they're not dynamic, but this mostly gets the job done. Since each shell is specific to each component then it would be easy to more explicitly define slots if need be.

And that's it. It's easy as importing your component just as you might normally:

`import VButton from '@/components/global/VButton`

But then when you use the component, passing a version prop notifies the __shell__ which __versioned__ component to use, and that should help curtail many breakages and allow adoption of the change to be handled over time:

```vue
<Button
  color="purple"
  version="v1"
  @click="handleClick">
  Click Me!
</Button>
```

_Note:_ This is an MVP for this concept. Someone can rightly criticize this approach for some of the following reasons:

* It's not globally useable
* It could be much strong written in pure Vue3 render functions (this example comes from a Nuxt 2.15 app using the nuxtjs/composition-api plugin, which is missing some features from Vue3, including `resolveComponent` which would most likely be able to solve this issue)
* This wouldn't be useful for more complex components

While these are true, I still think this is a very useful strategy especially if you are the type of dev who builds their own UI from scratch.

### Update

After a bit of messing out on codesandbox, I put together a working example that also uses the render function as the __shell__ component:

{% codesandbox intelligent-hill-iehm3 %}

_Note:_ In this Vue3 example `slots` can just be directly passed as the third parameter, but in Nuxt (and possibly Vue2 with the composition-api plugin) it needs to be: `map(slots, slot => slot)` using lodash.

### Update 2

After working with the concept for a bit I hit a particular tricky spot - emits.

The issue with emits is that, to my knowledge, there isn't a way to handle a passthrough of them as directly as you are able to with props or attributes.

This makes the shell component a bit less "user friendly" because each shell becomes more customized, and forces there to be two components that need to have emits maintained.

This is not optimal.

Then I remembered an article I read about an anti-pattern in Vue, but a common one in React, passing functions as props (I wish I could find the article to link to it).

Rather then:

```
@click="$emit('myFunction', value)
```

It becomes:

```
@click="myFunction(value)"

// in <script>
props: {
  myFunction: Function
}
```

I will say that this strategy is helpful on high-level components, but very low level components, like a button or input wrapper, would probably still be best served using emits in two places so that their events are easily consumed.