Let’s explore the reasons behind moving away from filters and how Vue 3 encourages alternative ways for achieving the same functionality.
Vue 3 officially launched in September 2020, and support for its predecessor, Vue 2, ended in December 2023. One significant shift in Vue 3 was the deprecation of filters, a feature commonly used in Vue 2 for formatting text and numbers within templates.
In this article, we’ll explore the reasons behind moving away from filters and how Vue 3 encourages alternative ways for achieving the same functionality.
In Vue 2, filters were primarily used to apply common text formatting directly in the template, such as capitalizing strings, formatting dates or adjusting number precision. Filters could be applied locally or globally and were used by chaining them in the template expressions with a pipe (|
) symbol. For example:
<template>
<p>{{ message | capitalize }}</p>
</template>
In the above example, capitalize
could be a custom filter that capitalizes the first letter of the message
data string.
<template>
<p>{{ message | capitalize }}</p>
</template>
<script>
export default {
data: {
message: 'hello world',
}
filters: {
capitalize(value) {
return value.toUpperCase();
}
}
}
</script>
While filters like the above provided a convenient way to handle formatting issues directly in templates, they had limitations, especially concerning their reusability and composability across components. Furthermore, the Vue migration documentation notes that while filters do appear to be a convenience, they require a custom syntax that breaks the assumption of expressions inside curly braces being “just JavaScript,” which has both learning and implementation costs.
To achieve the same functionality as filters in Vue 3, the Vue migration documentation recommends replacing them with either method calls or computed properties.
Computed properties are ideal for transforming data based on reactive dependencies. Computed properties are cached based on the dependencies they depend on and only re-evaluate when some of their dependencies have changed. Here is an example of replacing the capitalize
filter example we shared above with a computed property named capitalizedMessage
:
<template>
<p>{{ capitalizedMessage }}</p>
</template>
<script setup>
import { computed } from "vue";
const message = ref("hello world");
const capitalizedMessage = computed(() => {
return message.value.toUpperCase();
});
</script>
In the above example, the capitalizedMessage
computed property takes the message
ref as a dependency and transforms its value to uppercase.
For non-reactive or one-time data transformations, methods can be used in place of computed properties. This approach is similar to using filters, but methods have to be invoked explicitly in the template:
<template>
<p>{{ capitalize(message) }}</p>
</template>
<script setup>
const capitalize = (value) => {
return value.toUpperCase();
};
</script>
In the above example, the formatMessage()
method takes a value as input and transforms it to uppercase. This method can be invoked explicitly in the template by passing the message
value as an argument within the {{ }}
interpolation syntax. This approach is suitable for non-reactive or one-time data transformations, providing similar functionality to filters in Vue 2.
In Vue 2, global filters could be created to define reusable filters that could be accessed from any component within an application. Here’s how we might have set up a global filter in Vue 2, using the capitalize
filter as an example:
import Vue from "vue";
Vue.filter("capitalize", function (value) {
return value.toUpperCase();
});
const app = new Vue({
el: "#app",
});
Migrating global filters to methods and computed properties that are to be defined in every component that uses the filter won’t be straightforward. In cases like this, the Vue migration documentation encourages making global filters available to all components through globalProperties. This allows us to define global properties or methods that are accessible throughout our Vue application.
Here’s an example of defining a capitalize()
method as a global property under a $filters
object.
import { createApp } from "vue";
const app = createApp({});
app.config.globalProperties.$filters = {
capitalize(value) {
return value.toUpperCase();
},
};
app.mount("#app");
With this approach, the capitalize()
method becomes accessible as $filters.capitalize()
in any component of our Vue application.
<template>
<div>
<p>{{ message }}</p>
<p>{{ $filters.capitalize(message) }}</p>
</div>
</template>
<script setup>
import { ref } from "vue";
const message = ref("hello world");
</script>
By deprecating filters and encouraging the use of computed properties and methods, this change has helped improve the Vue framework’s overall consistency and reactivity. Computed properties offer a robust solution for reactive data transformations, optimizing performance by re-evaluating only when necessary, while methods provide flexibility for more straightforward, non-reactive transformations.
For more details, be sure to check Vue’s migration guide on filters.
Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.