The 101 guide to Script Setup in Vue 3
With the release of the Composition API, the new component option setup
has also made its way into Vue. With this, we no longer need other options in most cases and write all our code within this function. The idea of <script setup
is that you want to get rid of the unnecessary wrapper and the other old component options so you can write components simpler and focused.
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup() {
const count = ref<number>(0)
function increment() {
count.value++
}
return {
count,
increment
}
}
})
</script>
<script setup lang="ts">
import { ref } from 'vue'
const count = ref<number>(0)
function increment() {
count.value++
}
</script>
With the <script setup>
syntax we can write our component logic more compactly. Even a return
statement is no longer needed. Every variable and method we define is automatically provided in the template.
We can also use imported data like functions and even components directly. Components don`t even have to be registered separately anymore:
<script setup lang="ts">
import { ref } from 'vue'
import { format } from '@/utils/currency'
import MyComponent from '@/components/MyComponent.vue'
const count = ref<number>(0)
function increment() {
count.value++
}
</script>
<template>
<button @click="increment">{{ format(count) }}</button>
<MyComponent />
</template>
# Props and Events
To declare options like props
and emits
we have to use so called compiler macros which are automatically available inside <script setup>
.
<script setup lang="ts">
const props = defineProps({
msg: String
})
const emit = defineEmits(['update', 'remove'])
</script>
The two functions do not need to be imported and are compiled away when <script setup>
is preprocessed.
defineProps
takes the same value as the props
option, while defineEmits
takes the same value as the emits
option.
If we are already using TypeScript in our application anyway, then we can declare props
and emits
with a plain type syntax as well:
const props = defineProps<{
msg: string
count?: number
}>()
const emit = defineEmits<{
(e: 'update', id: number): void
(e: 'remove'): void
}>()
Both approaches cannot be combined, i.e. either we use the generic or the function brackets syntax, but never both at the same time. If we decide to go the TypeScript way, it is important to mention that we cannot currently use imported types or interfaces, but only type literals (see example) or types/interfaces defined in the same file.
In the case of events, we can specify the parameters more precisely ourselves and thus write type-safe code.
# Default values for props
Above in the example we have marked the prop count
as optional, but here we lack a possibility to specify a default value. For this reason there is another compiler macro called withDefaults
.
interface Props {
msg: string
count?: number
}
const props = withDefaults(defineProps<Props>(), {
count: 0
})
If withDefaults
is not used, but a prop is marked as optional in the type, Vue treats this prop
as required.
# Using Slots and Attributes
Normally, slots and attributes in components are used directly in the template via $slots
and $attrs
and rarely if ever needed in script
. If it should be necessary nevertheless, Vue provides appropriate functions with useSlots
and useAttrs
.
<script setup lang="ts">
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
</script>
# Use additional options
In some cases it is necessary that we declare custom options in our components. Vuelidate's validations
option is just one popular example. These other options, which include setting a component name via name
, are not possible with <script setup>
.
The solution to these problems is to use an additional script
tag. The two blocks are then automatically merged when the *.vue
file is compiled.
<script>
// executed only once
export const componentName = 'MyComponent';
export default {
name: componentName
inheritAttrs: false,
customOptions: {}
}
</script>
<script setup>
// executed for each component instance
</script>
# Access from the outside?
If we use <script setup>
every variable and method we define is automatically provided in the template, but the component itself is closed to the outside. So we cannot access the properties and functions declared inside <script setup>
via the instance of a component, for example when using $parent
or template refs.
To explicitly expose something, we need to use another compiler macro: defineExpose
.
<script setup lang="ts">
import { ref } from 'vue'
const count = ref<number>(0)
function increment() {
count.value++
}
defineExpose({
count,
increment
})
</script>
# Top-level await
In components we often need to request data asynchronously from a service. In <script setup>
we have the await
keyword available for this at the top level.
A component defined in this way must be used with the suspense
component so that Vue can take care of resolving the asynchrony and load the component properly.
<script setup>
const user = await fetch(`/users/1`).then((d) => d.json())
</script>
<template>
<Suspense>
<AsyncComponent />
</Suspense>
</template>
Warning: Suspense is currently still an experimental feature, which is not recommended for productive applications.
# Availability outside of Vue 3
If you are not lucky enough to be working with Vue 3 already, you will be happy to know that there is a fantastic plugin that makes <script setup>
available in Vue 2 as well as Nuxt, Vite or the Vue CLI: https://github.com/antfu/unplugin-vue2-script-setup
Related Articles
The new Provide and Inject in Vue 3
Getting stuck into the prop drilling? Learn how provide/inject can make your components more flexible and independent in this short tutorial.
Jul 18, 2022
Going 3D with Trois.js and Vue 3
Learn about Trois.js, a JS library to render 3D scenes in Vue. In this article, we're learning the basics of using Trois.js in a Vite + Vue 3 app
Nov 16, 2021
Use Composition API to easily handle API requests in Vue.js
Not sure how to organize your API client in Vue.js? Learn this simple technique on how to do it using the new Composition API and make it easy.
Jul 6, 2020
Sponsors
VueDose is proudly supported by its sponsors. If you enjoy it, consider supporting it to ensure the project maintainability.