Composition API definition
The Composition API is a collection of functions used to write components and composables in Vue. It is also used to describe one of the two main styles to write components, the other being the Options API. Components written using the Composition API use either <script setup> or an explicit setup() function.
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 type signature is: function useAttrs(): Record<string, unknown>
useSlots() returns slots as callable functions
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 type signature is: function useSlots(): Record<string, (...args: any[]) => VNode[]>. If using TypeScript, defineSlots() should be preferred instead.
useModel() helper for v-model binding
useModel() is the underlying helper that powers defineModel(). If using <script setup>, defineModel() should be preferred instead. Available in 3.4+. Type signature: function useModel(props: Record<string, any>, key: string, options?: DefineModelOptions): ModelRef. The DefineModelOptions type is: type DefineModelOptions<T = any> = { get?: (v: T) => any, set?: (v: T) => any }. The ModelRef type is: type ModelRef<T, M extends PropertyKey = string, G = T, S = T> = Ref<G, S> & [ModelRef<T, M, G, S>, Record<M, true | undefined>].
useModel() example with props and emits
useModel() can be used in non-SFC components such as when using raw setup() function. It expects the props object as the first argument and the model name as the second argument. The optional third argument can be used to declare custom getter and setter for the resulting model ref. Unlike defineModel(), you are responsible for declaring the props and emits yourself. Example: export default { props: ['count'], emits: ['update:count'], setup(props) { const msg = useModel(props, 'count'); msg.value = 1 } }
useId() generates unique accessibility IDs
useId() is used to generate unique-per-application IDs for accessibility attributes or form elements. Available in 3.5+. Type signature: function useId(): string. IDs generated by useId() are unique-per-application. 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 use in SSR applications without hydration mismatches.
useId() example with form elements
Example using useId() to generate a unique ID for a form label and input: import { useId } from 'vue'; const id = useId(); <template><form><label :for="id">Name:</label><input :id="id" type="text" /></form></template>
useId() configuration with app.config.idPrefix
If you have more than one Vue application instance on the same page, you can avoid ID conflicts by giving each app an ID prefix via app.config.idPrefix.
useId() should not be called inside computed()
useId() should not be called inside a computed() property as it may cause instance conflicts. Instead, declare the ID outside of computed() and reference it within the computed function.
setup() hook purpose and use cases
The setup() hook serves as the entry point for Composition API usage in components in two cases: 1) using Composition API without a build step, or 2) integrating with Composition-API-based code in an Options API component. When using Composition API with Single-File Components, <script setup> is strongly recommended for a more succinct and ergonomic syntax.
setup() return value and template exposure
Reactive state can be declared using Reactivity APIs and exposed to the template by returning an object from setup(). The properties on the returned object will also be made available on the component instance if other options are used.
Ref unwrapping 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. They are also unwrapped in the same way when accessed on this.
setup() does not have 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() 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.
Accessing props in setup()
The first argument in the setup function is the props argument. Props inside of a setup function are reactive and will be updated when new props are passed in.
toRefs() and toRef() for destructured props
Use toRefs() to turn props into an object of refs and then destructure it, where each destructured variable becomes a ref that tracks the corresponding prop. Alternatively, use toRef(props, 'propertyName') to turn a single property on props into a ref while retaining reactivity.
Setup Context object second argument
The second argument passed to the setup function is a Setup Context object. The context object exposes: attrs (Non-reactive object, equivalent to $attrs), slots (Non-reactive object, equivalent to $slots), emit (Function, equivalent to $emit), and expose (Function to expose public properties).
Setup Context is non-reactive and safely destructurable
The context object is not reactive and can be safely destructured. You can destructure it as: setup(props, { attrs, slots, emit, expose }).
attrs and slots are stateful and always updated
attrs and slots are stateful objects that are always updated when the component itself is updated. You should avoid destructuring them and always reference properties as attrs.x or slots.x. Unlike props, the properties of attrs and slots are not reactive. If you intend to apply side effects based on changes to attrs or slots, you should do so inside an onBeforeUpdate lifecycle hook.
expose() function for limiting public properties
expose is a function that can be used to explicitly limit the properties exposed when the component instance is accessed by a parent component via template refs. Calling expose() without arguments makes the instance "closed" and does not expose anything to the parent. Pass an object to expose() to selectively expose local state.
Exposing methods from setup with render function
If setup returns a render function and you want to expose methods to the parent component via template refs, call expose() and pass an object containing the methods you want to expose. The methods will then be available in the parent component via a template ref.
setup() example with reactive state and template
Example showing setup() with 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>
setup() example with props access
Example showing props access in setup: export default { props: { title: String }, setup(props) { console.log(props.title) } };
setup() example with toRefs and toRef
Example showing destructured props with retained reactivity: import { toRefs, toRef } from 'vue'; export default { setup(props) { const { title } = toRefs(props); console.log(title.value); // OR const title = toRef(props, 'title'); } };
setup() example with Setup Context destructuring
Example showing context object destructuring: export default { setup(props, { attrs, slots, emit, expose }) { ... } };
setup() example with expose() for limiting properties
Example showing expose() usage: export default { setup(props, { expose }) { expose(); const publicCount = ref(0); const privateCount = ref(0); expose({ count: publicCount }); } };
setup() example with render function and expose()
Example showing render function with exposed methods: 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); } };
Vue 3 vs Vue 2 API similarity
The majority of Vue APIs are shared between Vue 2 and Vue 3, so most Vue 2 knowledge will continue to work in Vue 3. Composition API was originally a Vue-3-only feature but has been backported to Vue 2 and is available in Vue 2.7.