Black Friday Killer Deal
Get the Ultimate Vue Bundle for 64% Off

All the courses you need to master Vue ecosystem + the certificate to prove it

Get the Offer

As Vue continues to become more and more widespread, there are several best practices emerging and becoming standard.

For this article, I included some tips from a few great resources.

Hopefully, these best practices can help you write better VueJS code. Not only will that make all your fellow developers love you, but you’ll definitely thank yourself down the line for making your life easier.

Okay, enough introduction. Let’s dive right in.

1. Always use inside v-for

Using the key attribute with the v-for directive helps your application be constant and predictable whenever you want to manipulate the data.

This is necessary so that Vue can track your component state as well as have a constant reference to your different elements. An example where keys are extremely useful is when using animations or Vue transitions.

Without keys, Vue will just try to make the DOM has efficient as possible. This may mean elements in the v-for may appear out of order or their behavior will be less predictable. If we have a _unique_ key reference to each element, then we can better predict how exactly our Vue application will handle DOM manipulation.

<template>
  <!-- BAD -->
  <div v-for="product in products">{{ product }}</div>

  <!-- GOOD! -->
  <div v-for="product in products" :key="product.id">{{ product }}</div>
</template>

2. Use kebab-case for events

When it comes to emitting custom events, it’s always best to use kebab-case. This is because in the parent component, that’s the same syntax we use to listen to that event.

So for consistency across our components, and to make your code more readable, stick to using kebab-case in both places.

PopupWindow.vue
this.$emit("close-window");
ParentComponent.vue
<template>
<popup-window @close-window='handleEvent()' />
</template>

3. Declare props with camelCase and use kebab-case in templates

This best practice simply just follows the conventions for each language. In JavaScript, camelCase is the standard and in HTML, it’s kebab-case Therefore, we use them accordingly.

Luckily for us, VueJS converts between kebab-case and camelCase for us so we don’t have to worry about anything besides actually declaring them.

In JavaScript, camelCase is the standard and in HTML, it’s kebab-case Therefore, we use them accordingly.

<template>
  <PopupWindow title-text="hello world" />
</template>

<script>
export default {
  props: {
    titleText: String,
  },
}
</script>

4. Data should always return a function

When declaring component data in the Options API, the data option should always return a function. If it does not, and we just simply return an object, then that data will be shared across all instances of the component.

export default {
  data() {
    // <---
    return {
      name: 'My Window',
      articles: [],
    }
  },
}

However, most of the time, the goal is to build reusable components, so we want each object to return a unique object. We accomplish this by returning our data object inside a function.

5. Don’t use v-if with v-for elements

It’s super tempting to want to use v-if with v-for in order to filter elements of an array.

<!--BAD-->
<div
   v-for='product in products'
   v-if='product.price < 500'
>

The problem with this is that VueJS prioritizes the v-for directive over the v-if directive. So under the hood, it loops through every element and THEN checks the v-if conditional.

This means that even if we only want to render a few elements from a list, we’ll have to loop through the entire array.

This is no good.

A smarter solution would be to iterate over a computed property. The above example would look something like this.

This means that even if we only want to render a few elements from a list, we’ll have to loop through the entire array.

<template>
  <div v-for="product in cheapProducts">{{ product }}</div>
</template>

<script>
export default {
  computed: {
    cheapProducts: () => {
      return this.products.filter(function (product) {
        return product.price < 100
      })
    },
  },
}
</script>

This is good for a few reasons.

  • Rendering is much more efficient because we don’t loop over every item
  • The filtered list will only be re-evaluated when a dependency changes
  • It helps separate our component logic from the template, making our component more readable

6. Validate your props with good definitions

This is arguably the most important best practice to follow.

Why is it important?

Well. It basically saves future you from current you. When designing a large scale project, it’s easy to forget exactly the exact format, type, and other conventions you used for a prop.

And if you’re in a larger dev team, your coworkers aren’t mind-readers so make it clear to them how to use your components! So save everyone the hassle of having to painstakingly trace your component to determine a prop’s formatting and please just write prop validations.

Check out this example from the Vue docs.

export default {
  props: {
    status: {
      type: String,
      required: true,
      validator: function (value) {
        return (
          ['syncing', 'synced', 'version-conflict', 'error'].indexOf(value) !==
          -1
        )
      },
    },
  },
}

7. Use PascalCase or kebab-case for components

A common naming convention for components is to use PascalCase or kebab-case.

No matter which one you choose for your project, it’s most important that you stay consistent all the time.

PascalCase works best because it is supported by most IDE autocomplete features.

# BAD

mycomponent.vue  
myComponent.vue  
Mycomponent.vue

# GOOD

MyComponent.vue

8. Base components should be prefixed accordingly

Another naming convention is focused around naming base components – or components that are purely presentational and help setup common styles across your app.

According to the Vue style guides, base components are components that only contain…

  • HTML elements
  • Additional base components
  • 3rd party UI components

The best practice for naming these components is to give them the prefix “Base”, “V”, or “App”.

Once again, it’s alright to use either of these as long as you stay consistent throughout your project.

The purpose of this naming convention is that it keeps your base components together in your file system.

Also, using a webpack import function, you can search for components matching your naming convention pattern and automatically import all of them as globals in your Vue project.

9. Components declared and used ONCE should have the prefix “The”

Similar to base components, single instance components (ones used once per page and does not accept props) have their own naming convention.

These components are specific to your app and are normally things like a header, sidebar, or footer.

There should only ever be one active instance of this component.

  • TheHeader.vue
  • TheFooter.vue
  • TheSidebar.vue
  • ThePopup.vue

10. Stay consistent with your directive shorthand A common technique among

Vue developers is to use shorthand for directives. For example,

  • @ is short for v-on:
  • : is short for v-bind
  • # is short for v-slot

It is great to use these shorthands in your Vue project. But to create some sort of convention across your project, you should either always use them or never use them. This will make your project more cohesive and readable.

11. Don’t call a method on created AND watch

A common mistake Vue developers make (or maybe it was just me) is they unnecessarily call a method in created and watch.

The thought behind this is that we want to run the watch hook as soon as a component is initialized.

<script>
// BAD!
  export default {
   created: () {
    this.handleChange()
   },
   methods: {
    handleChange() {
     // stuff happens
    }
   },
   watch () {
    property() {
     this.handleChange()
    }
   }
  }
</script>

However, there Vue has a built in solution for this. And it’s a property of Vue watchers that we often forget.

All we have to do is restructure our watcher a little bit and declare two properties:

  • handler (newVal, oldVal) – this is our watcher method itself
  • immediate: true – this makes our handler run when our instance is created
<script>
export default {
 methods: {
  handleChange() {
   // stuff happens
  }
 },
 watch () {
  property {
   immediate: true
   handler() {
    this.handleChange()
   }
  }
 }
}
</script>

12. Template expressions should only have basic JavaScript expressions

It’s natural to want to add as much inline functionality in your templates as possible. But this makes our template less declarative and more complex. Meaning that our template just gets extremely cluttered.

For this, let’s check out another example from the Vue style guide.Look how confusing it is.

<template>
  <!--BAD-->
  {{
    fullName
      .split(' ')
      .map(function (word) {
        return word[0].toUpperCase() + word.slice(1)
      })
      .join(' ')
  }}
</template>

Basically, we want everything in our template to be intuitive as to what it is displaying. To keep this, we should refactor complex expressions into appropriately named component options. Another benefit of separating out complex expressions is that it means these values can be reused.

<template>{{ normalizedFullName }}</template>

<script>
export default {
  // The complex expression has been moved to a computed property
  computed: {
    normalizedFullName: function () {
      return this.fullName
        .split(' ')
        .map(function (word) {
          return word[0].toUpperCase() + word.slice(1)
        })
        .join(' ')
    },
  },
}
</script>

Conclusion

And there you have it.

Those were 12 of the most common best practices that will make your Vue code more maintainable, readable, and more professional. Hopefully these tips were useful to you (because they’re definitely things that I always want to remember).