setup() return object exposes reactive state
Reactive state declared using Reactivity APIs can be exposed to the template by returning an object from setup(). The properties on the returned object are made available on the component instance and in the template.
Example: expose() to control public properties
export default {
setup(props, { expose }) {
expose()
const publicCount = ref(0)
const privateCount = ref(0)
expose({ count: publicCount })
}
}
Example: toRefs() for destructuring props
import { toRefs } from 'vue'
export default {
setup(props) {
const { title } = toRefs(props)
console.log(title.value)
}
}
Refs auto-unwrap in templates from setup()
Refs returned from setup() are automatically shallow unwrapped when accessed in the template, so you do not need to use .value when accessing them in the template. They are also unwrapped in the same way when accessed on this in Options API hooks.
expose() function limits exposed properties
The expose function can be used to explicitly limit the properties exposed when the component instance is accessed by a parent component via template refs. Calling expose() with no arguments makes the instance "closed" and exposes nothing. Call expose with an object to selectively expose properties.
Example: setup() with Setup Context destructuring
export default {
setup(props, { attrs, slots, emit, expose }) {
// Access context values
}
}
attrs and slots are stateful but not reactive
attrs and slots are stateful objects that are always updated when the component itself is updated, but their properties are not reactive. You should avoid destructuring them and always reference properties as attrs.x or slots.x. If you need to apply side effects based on changes to attrs or slots, do so inside an onBeforeUpdate lifecycle hook.
Example: toRef() for single prop
import { toRef } from 'vue'
export default {
setup(props) {
const title = toRef(props, 'title')
}
}
First argument to setup() is props
The first argument in the setup function is the props argument. Props inside setup are reactive and will be updated when new props are passed in.
setup() has no access to component instance
setup() itself does not have access to the component instance; this will have a value of undefined inside setup(). You can access Composition-API-exposed values from Options API, but not the other way around.
setup() hook entry point for Composition API
The setup() hook serves as the entry point for Composition API usage in components when using Composition API without a build step or when integrating with Composition-API-based code in an Options API component. For Single-File Components, <script setup> is strongly recommended for more succinct and ergonomic syntax.
setup() can return a render function
setup() can return a render function which can directly make use of the reactive state declared in the same scope. This allows creating components without a template.
Example: setup() render function with expose()
import { h, ref } from 'vue'
export default {
setup(props, { expose }) {
const count = ref(0)
const increment = () => ++count.value
expose({
increment
})
return () => h('div', count.value)
}
}
ref() function creates reactive reference
The ref() function from the Reactivity APIs is used to declare reactive state in setup(). It accepts any value type and wraps it in a reactive reference object with a .value property.
Example: setup() accessing props
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
Example: setup() with basic reactive state
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return {
count
}
},
mounted() {
console.log(this.count) // 0
}
}
<template>
<button @click="count++">{{ count }}</button>
</template>
Setup Context can be safely destructured
The Setup Context object is not reactive and can be safely destructured: setup(props, { attrs, slots, emit, expose })
Setup Context is second argument to setup()
The second argument passed to setup() is a Setup Context object that exposes other values useful inside setup: attrs (non-reactive, equivalent to $attrs), slots (non-reactive, equivalent to $slots), emit (function, equivalent to $emit), and expose (function to expose public properties).
Use toRefs() or toRef() to destructure props while retaining reactivity
To destructure props or pass a prop into an external function while retaining reactivity, use toRefs() to turn props into an object of refs then destructure, or use toRef() to turn a single property on props into a ref.
Destructuring props loses reactivity
If you destructure the props object in setup, the destructured variables will lose reactivity. It is recommended to always access props in the form of props.xxx to maintain reactivity.
Render function return prevents exposing methods to parent
Returning a render function from setup prevents returning an object with methods, which means methods cannot be exposed to the parent component via template refs.
setup() must return synchronously
setup() should return an object synchronously. The only case when async setup() can be used is when the component is a descendant of a Suspense component.
Type-only props and emits declarations in <script setup>
Props and emits can be declared using pure-type syntax by passing a literal type argument to `defineProps` or `defineEmits`. In Vue 3.5+, the alternative more succinct syntax for `defineEmits` is available: const emit = defineEmits<{change: [id: number], update: [value: string]}>(). Only one of runtime declaration or type declaration can be used; using both at the same time results in a compile error. When using type declaration, the equivalent runtime declaration is automatically generated from static analysis.
useSlots and useAttrs functions
`useSlots` and `useAttrs` are runtime functions that can be imported from 'vue' and used inside `<script setup>` to access slots and attrs respectively. They return the equivalent of setupContext.slots and setupContext.attrs. They are rarely needed in `<script setup>` since you can access $slots and $attrs directly in the template.
defineProps and defineEmits options cannot reference local variables
The options passed to `defineProps` and `defineEmits` are hoisted out of setup into module scope. Therefore, the options cannot reference local variables declared in setup scope. Doing so will result in a compile error. However, they can reference imported bindings since they are in module scope.
defineProps and defineEmits compiler macros
`defineProps` and `defineEmits` are compiler macros automatically available inside `<script setup>` and do not need to be imported. They are compiled away when `<script setup>` is processed. `defineProps` accepts the same value as the `props` option, while `defineEmits` accepts the same value as the `emits` option. They provide proper type inference based on the options passed.
Combining <script setup> and normal <script> restrictions
Do NOT use a separate `<script>` section for options that can already be defined in `<script setup>`, such as props and emits. Variables created inside `<script setup>` are not added as properties to the component instance, making them inaccessible from the Options API. Mixing APIs in this way is strongly discouraged.
Combining <script setup> and normal <script>
A normal `<script>` block can be used alongside `<script setup>` in these cases: declaring options that cannot be expressed in `<script setup>` (like inheritAttrs or custom options), declaring named exports, or running side effects that should only execute once. A normal `<script>` executes in module scope (only once), while `<script setup>` executes in setup() scope (for each instance).
defineOptions macro for component options in <script setup>
`defineOptions()` is available in Vue 3.3+ and allows declaring component options directly inside `<script setup>` without needing a separate `<script>` block. This is a macro; the options are hoisted to module scope and cannot access local variables in `<script setup>` that are not literal constants.
<script setup> basic syntax and attribute
To opt into <script setup> syntax, add the `setup` attribute to the `<script>` block. The code inside is compiled as the content of the component's `setup()` function. Unlike normal `<script>` which executes once when the component is first imported, code inside `<script setup>` executes every time an instance of the component is created.
withDefaults compiler macro for type-based prop defaults
In Vue 3.4 and below, when using type-based `defineProps` declaration, use the `withDefaults` compiler macro to declare default values. Default values for mutable reference types (arrays or objects) should be wrapped in functions to avoid accidental modification and external side effects, ensuring each component instance gets its own copy.
Reactive Props Destructure in Vue 3.5+
In Vue 3.5 and above, variables destructured from the return value of `defineProps` are reactive. Vue's compiler automatically prepends `props.` when code in the same `<script setup>` block accesses variables destructured from `defineProps`. You can also use JavaScript's native default value syntax to declare default values for props when destructuring.
ref() function for reactive state in Composition API
The `ref()` function creates a reactive reference for values. It accepts any JavaScript value type including primitives (strings, numbers, booleans), objects, arrays, and functions. When you mutate the ref's `.value` property, it triggers reactivity. In templates, refs are automatically unwrapped so you use them without `.value`.
<script setup> does not support In-DOM Root Component Template
`<script setup>` does not support In-DOM Root Component Template.
<script setup> provides better performance and IDE support
`<script setup>` provides several advantages over normal `<script>` syntax: more succinct code with less boilerplate, ability to declare props and emitted events using pure TypeScript, better runtime performance (the template is compiled into a render function in the same scope without an intermediate proxy), and better IDE type-inference performance (less work for the language server).
Refs are automatically unwrapped in <script setup> templates
When refs are referenced in templates within a component using `<script setup>`, they are automatically unwrapped. You do not need to use `.value` in template expressions.
@vue-generic directive for explicit type passing
Use the @vue-generic directive in templates to pass explicit types for generic components when the type cannot be inferred. Example: <!-- @vue-generic {import('@/api').Actor} --> <ApiSelect ... /> explicitly passes the Actor type.
Generics example with constraints and defaults
Example: <script setup lang="ts" generic="T extends string | number, U extends Item"> where Item is imported. This declares two generic parameters with constraints, allowing defineProps<{id: T, list: U[]}>()
Generic component references and vue-component-type-helpers
To use a reference to a generic component in a `ref`, use the `vue-component-type-helpers` library and `ComponentExposed` type, as `InstanceType` does not work with generics. Example: ref<ComponentExposed<typeof genericComponent>>()
<script setup> import statements and aliases
Import statements in Vue follow the ECMAScript module specification. You can use aliases defined in your build tool configuration, such as: import { componentB } from './Components', import { componentC } from '@/Components', or import { componentD } from '~/Components'.
Top-level await in <script setup>
Top-level `await` can be used inside `<script setup>`. The resulting code is compiled as `async setup()`. The awaited expression is automatically compiled in a format that preserves the current component instance context after the `await`. `async setup()` must be used in combination with `Suspense`, which is currently an experimental feature.
Generics in <script setup>
Generic type parameters can be declared using the `generic` attribute on the `<script setup>` tag in TypeScript. Example: <script setup lang="ts" generic="T"> declares a generic component. Multiple parameters, extends constraints, default types, and imported type references are supported.
<script setup> cannot use src attribute
`<script setup>` cannot be used with the `src` attribute. Due to differences in module execution semantics, code inside `<script setup>` relies on the context of an SFC. When moved into external .js or .ts files, it may lead to confusion for both developers and tools.
Top-level bindings in <script setup> are exposed to template
Any top-level bindings declared inside `<script setup>`, including variables, function declarations, and imports, are directly usable in the template without needing to be exposed via a methods option or other mechanism.
hasInjectionContext() function signature
The type signature is: function hasInjectionContext(): boolean
inject() must be called synchronously during setup phase
Similar to lifecycle hook registration APIs, inject() must be called synchronously during a component's setup() phase.
inject() with factory function for expensive values
The second argument to inject() can be a factory function that returns values that are expensive to create. When using a factory function, true must be passed as the third argument (treatDefaultAsFactory) to indicate the function should be used as a factory instead of the value itself.
inject() example with various patterns
Example of using inject() in <script setup>:
```vue
<script setup>
import { inject } from 'vue'
import { countSymbol } from './injectionSymbols'
// inject static value without default
const path = inject('path')
// inject reactive value
const count = inject('count')
// inject with Symbol keys
const count2 = inject(countSymbol)
// inject with default value
const bar = inject('path', '/default-path')
// inject with function default value
const fn = inject('function', () => {})
// inject with default value factory
const baz = inject('factory', () => new ExpensiveObject(), true)
</script>
```
provide() example with static value, reactive value, and Symbol keys
Example of using provide() in <script setup>:
```vue
<script setup>
import { ref, provide } from 'vue'
import { countSymbol } from './injectionSymbols'
// provide static value
provide('path', '/project/')
// provide reactive value
const count = ref(0)
provide('count', count)
// provide with Symbol keys
provide(countSymbol, count)
</script>
```
inject() function signatures for different cases
The inject() function has three overloads:
1. Without default value: function inject<T>(key: InjectionKey<T> | string): T | undefined
2. With default value: function inject<T>(key: InjectionKey<T> | string, defaultValue: T): T
3. With factory function: function inject<T>(key: InjectionKey<T> | string, defaultValue: () => T, treatDefaultAsFactory: true): T
Vue walks up the parent chain to locate a provided value with a matching key. If multiple components provide the same key, the one closest to the injecting component shadows higher ones. If no matching value is found, inject() returns undefined unless a default value is provided.
hasInjectionContext() function purpose
hasInjectionContext() returns true if inject() can be used without warning about being called in the wrong place (e.g. outside of setup()). This method is designed for libraries that want to use inject() internally without triggering a warning to end users. It is only supported in Vue 3.3+.
InjectionKey for TypeScript type synchronization
When using TypeScript with provide() and inject(), the key can be a symbol casted as InjectionKey, which is a Vue-provided utility type that extends Symbol. This allows synchronizing the value type between provide() and inject().
provide() function signature and purpose
The provide() function provides a value that can be injected by descendant components. Its type signature is: function provide<T>(key: InjectionKey<T> | string, value: T): void. It takes two arguments: the key (which can be a string or a symbol) and the value to be injected. The provide() function must be called synchronously during a component's setup() phase.
useModel() example with props and emits
Example of useModel() in a non-SFC component:
export default {
props: ['count'],
emits: ['update:count'],
setup(props) {
const msg = useModel(props, 'count')
msg.value = 1
}
}
useTemplateRef() syncs ref with template elements
useTemplateRef() returns a shallow ref whose value will be synced with the template element or component with a matching ref attribute. It is available in Vue 3.5+. The function signature is: function useTemplateRef<T>(key: string): Readonly<ShallowRef<T | null>>. The key parameter is a string matching the ref attribute name in the template.
useTemplateRef() example with input focus
Example of useTemplateRef() in <script setup>:
<script setup>
import { useTemplateRef, onMounted } from 'vue'
const inputRef = useTemplateRef('input')
onMounted(() => {
inputRef.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
useSlots() returns parent-passed slots
useSlots() returns the slots object from the Setup Context, which includes parent passed slots as callable functions that return Virtual DOM nodes. It is intended to be used in <script setup> where the setup context object is not available. The function signature is: function useSlots(): Record<string, (...args: any[]) => VNode[]>. If using TypeScript, defineSlots() should be preferred instead.
useAttrs() returns fallthrough attributes
useAttrs() returns the attrs object from the Setup Context, which includes the fallthrough attributes of the current component. It is intended to be used in <script setup> where the setup context object is not available. The function signature is: function useAttrs(): Record<string, unknown>
useModel() helper for two-way binding
useModel() is the underlying helper that powers defineModel(). It is available in Vue 3.4+. The function signature is: function useModel(props: Record<string, any>, key: string, options?: DefineModelOptions): ModelRef. The DefineModelOptions type has optional get and set properties for custom getter and setter. useModel() can be used in non-SFC components with raw setup() function, accepting the props object as first argument and the model name as second argument. Unlike defineModel(), you are responsible for declaring the props and emits yourself.
useId() generates unique application-level IDs
useId() is used to generate unique-per-application IDs for accessibility attributes or form elements. It is available in Vue 3.5+. The function signature is: function useId(): string. Multiple calls in the same component will generate different IDs. Multiple instances of the same component calling useId() will also have different IDs. IDs are guaranteed to be stable across server and client renders for SSR applications without hydration mismatches.