·
3 min read

What's New in Vue 3.3

What's New in Vue 3.3 Image

Vue 3.3 "Rurouni Kenshin" is now available and "is focused on developer experience improvements".

In this article, I give an overview of the highlighted features in Vue 3.3. Read the changelog if you are interested in all changes of this new version.

Props Destructuring

Warning

This feature is experimental and requires explicit opt-in.

I think this is one of the coolest features of the new release. You can now destructure props without losing reactivity and also set default values:

<script setup lang="ts"> const { counter = 0, testId = 'counter' } = defineProps<{ counter?: number; testId?: string }>() </script>

In my opinion, this is a very clean and "natural" way to define your props. Previously you had to use toRefs in combination with withDefaults to achieve the same result:

<script setup lang="ts"> const { counter, testId } = toRefs( withDefaults(defineProps<{ counter?: number; testId?: string }>(), { counter: 0, testId: 'counter' }) ) </script>

defineModel

Warning

This feature is experimental and requires explicit opt-in.

Vue 3.3 provides a very elegant way to support two-way binding with v-model. Before 3.3 you had to write a lot of boilerplate code to support it:

<script setup> const props = defineProps(['modelValue']) const emit = defineEmits(['update:modelValue']) function onInput(event) { emit('update:modelValue', event.target.value) } </script> <template> <input :value="modelValue" @input="onInput" /> </template>

With 3.3 we can achieve the same functionality with less code:

<script setup> const modelValue = defineModel() </script> <template> <input v-model="modelValue" /> </template>

In this example, the defineModel macro automatically registers a prop modelValue and returns a ref that can be directly mutated. Additionally. it registers the update:modelValue event.

Improved TypeScript Type Support

In the past, only local types such as type literals and interfaces could be used in the type parameter position of the defineProps and defineEmits compiler macros.

The reason for this was that Vue needed to analyze the properties on the props interface to create runtime options. However, this limitation has been addressed in version 3.3. The Vue compiler can now handle imported types and a limited set of complex types:

<script setup lang="ts"> import type { Props } from './foo' // imported + intersection type defineProps<Props & { extraProp?: string }>() </script>

Generic Components

Your components can now accept generic type parameters via the generic attribute if you are using <script setup>:

<script setup lang="ts" generic="T"> defineProps<{ items: T[] selected: T }>() </script>

More Ergonomic defineEmits

Typing defineEmits was a bit verbose before 3.3:

<script setup lang="ts"> const emit = defineEmits<{ (e: 'foo', id: number): void (e: 'bar', name: string, ...rest: any[]): void }>() </script>

Vue 3.3 provides a "more ergonomic" way:

<script setup lang="ts"> const emit = defineEmits<{ foo: [id: number] bar: [name: string, ...rest: any[]] }>() </script>

Use console in the template

You can now use console in your template:

<script setup> import { ref } from 'vue' const count = ref(0) </script> <template> <h1>{{ count }}</h1> <button @click="console.log(++count)">Increment</button> </template>

In previous versions of Vue, this caused an error: TypeError: Cannot read properties of undefined (reading 'log')

Try it yourself

Conclusion

Vue got so much better with this new version. The new features improve the developer experience and I'm very excited to see how the framework further evolves with the next upcoming releases.

For more information, read the official announcement and the GitHub changelog.

If you liked this article, follow me on Twitter to get notified about new blog posts and more content from me.

Alternatively (or additionally), you can subscribe to my weekly Vue newsletter:

I will never share any of your personal data. You can unsubscribe at any time.

If you found this article helpful.You will love these ones as well.
Rendering Dynamic Markdown in Nuxt 3+ Image

Rendering Dynamic Markdown in Nuxt 3+

Analyze Memory Leaks in Your Nuxt App Image

Analyze Memory Leaks in Your Nuxt App

Self-Host Your Nuxt App With Coolify Image

Self-Host Your Nuxt App With Coolify

Simpler Two-Way Binding in Vue With defineModel Image

Simpler Two-Way Binding in Vue With defineModel