In this article, I've curated a collection of useful code snippets and tips that I frequently find myself searching for in the Vue docs or digging up from my existing codebase during development. Having all these snippets in one place should make it easier to reference and use them as quick copy-paste examples, ultimately saving valuable time when developing with Vue 3 and TypeScript.
Let's dive in!
Define a component
Here's an example of a component definition using the <script setup>
syntax. The defineProps
and defineEmits
macros are used to declare the component's prop
s and emitted event
s, respectively. The withDefaults
helper allows us to set default values for the prop
s. We can define this as a custom code snippet in our code editor to serve as a starting point when we're creating a new component.
<template>
<div class="mb-4 flex items-center rounded bg-blue-50 p-4 text-blue-800">
<div class="text-sm font-medium">
{{ text }}
</div>
<button
v-if="closeable"
type="button"
class="-m-1.5 ms-auto inline-flex size-8 items-center justify-center rounded p-1.5 hover:bg-blue-100"
@click="close"
>
✕
</button>
</div>
</template>
<script setup lang="ts">
defineOptions({
name: 'SimpleComponent'
})
type Props = {
text?: string
closeable?: boolean
}
const props = withDefaults(defineProps<Props>(), {
text: '',
closeable: false
})
const emit = defineEmits<{
'click:close': [value: string]
}>()
const close = () => {
emit('click:close', props.text)
}
</script>
Props validation
To validate prop
s, we have to use the defineProps
macro along with the PropType
utility. This allows us to specify the type and a validator function
that checks the prop
's value. Here, the text prop is validated to ensure it starts with the letter 'T'.
import { type PropType } from 'vue'
defineProps({
text: {
type: String as PropType<string>,
default: 'Test',
validator(value: string) {
return value.startsWith('T')
}
}
})
Define a template ref
Sometimes, we need a reference to a DOM
element within our template. We can achieve this using ref
.
<template>
<div ref="header" class="text-4xl">Header text</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const header = ref<HTMLElement | null>(null)
</script>
Define a component ref (get the type of a component)
Similarly, like DOM
element refs, there are times when we require a reference to a component within our template. For that, we can use the InstanceType
utility along with typeof
. This ensures that our component references are correctly typed.
<template>
<SimpleComponent ref="simpleComponentRef" :text="'Hello world!'" />
</template>
<script setup lang="ts">
import SimpleComponent from './SimpleComponent.vue'
import { ref } from 'vue'
type SimpleComponentType = InstanceType<typeof SimpleComponent>
const simpleComponentRef = ref<SimpleComponentType | null>(null)
</script>
In cases where the exact type of the component isn't available or isn't important, we can use the generic ComponentPublicInstance
that Vue exposes for us, i.e:
import { type ComponentPublicInstance } from 'vue'
const simpleComponentRef = ref<ComponentPublicInstance | null>(null)
Generic component type
In scenarios where we need to store and render components dynamically, we can use the generic Component
type. Such a scenario would be for example to store components in an object
or an array
so that we can render them dynamically in the template. By using shallowRef
, we ensure that Vue doesn't complain with [Vue warn]: Vue received a Component that was made a reactive object.
.
<template>
<Component :is="components.settings" />
</template>
<script setup lang="ts">
import { type Component, shallowRef } from 'vue'
import IconSettings from './IconSettings'
import IconHome from './IconHome'
const components = ref<Record<string, Component>>({})
// Later in our code
components.value.settings= IconSettings
components.value.home = IconHome
</script>
We can also use shallowRef
for a single component reference.
const settings = shallowRef(IconSettings)
Easy wrapper components with defineModel
I've previously written about how to create an input wrapper in Vue 3 by binding events manually. However, starting from Vue 3.4, there's now a simpler way to achieve this using defineModel
.
<template>
<div>
<label class="block">
{{ label }}
</label>
<input v-model="model" type="text" class="rounded border p-2" />
</div>
</template>
<script setup lang="ts">
defineOptions({
name: 'InputWrapper'
})
withDefaults(defineProps<{ label?: string }>(), {
label: ''
})
const model = defineModel<string>({ default: '' })
</script>
Working with disabled attribute inheritance
In Vue 3, event listeners, as well as style
and class
, are now part of all attributes. When we're working with inheritAttrs: false
, we often want to distribute different attributes across various parts of our components. In the following examples, we'll see how we can manage this.
Get listeners like in Vue 2
We can still extract event listeners specifically by using useAttrs
and a computed
property.
<template>
<div>
<button type="button" v-bind="listeners">✕</button>
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs } from 'vue'
defineOptions({
inheritAttrs: false
})
const allAttrs = useAttrs()
const listeners = computed(() => {
return Object.keys(allAttrs).reduce((acc, curr) => {
if (curr.startsWith('on')) {
return { ...acc, [curr]: allAttrs[curr] }
} else {
return acc
}
}, {})
})
</script>
Get all attrs without style and classes
Sometimes, it's useful to separate style
and classes
from the rest of the attributes. We can easily do this by using the spread operator to extract them from all attributes.
import { computed, useAttrs } from 'vue'
const allAttrs = useAttrs()
const attrs = computed(() => {
const { class: classes, style, ...rest } = allAttrs
return rest
})
Get only style from attrs
Similarly, we can extract any part we want from all attributes. Here's an example showing how we can extract style
.
import { type StyleValue, computed, useAttrs } from 'vue'
const allAttrs = useAttrs()
const style = computed(() => {
return allAttrs.style as StyleValue
})
That was it!
I hope these snippets and tips help streamline your development process with Vue 3 and TypeScript. If you have your own most-used snippets or find yourself frequently forgetting how to do certain things in Vue, feel free to share them in the comments.
Thanks for reading!
Top comments (1)
Than you for the awesome blog post
Having really awesome code snippets is essential. I think every developer should define snippets for tasks they frequently perform. It helps so much while coding, especially for me, since I often can't remember the exact syntax. I also have many snippets that I use for writing tests.