Skip to main content
Helper functions for accessing component context and managing component state in the Composition API.

useSlots()

Returns the slots object from the setup context. Type Signature:
function useSlots(): SetupContext['slots']

Returns

The slots object with the same structure as $slots in the Options API.

Example

<script setup>
import { useSlots } from 'vue'

const slots = useSlots()

// Check if a slot exists
if (slots.header) {
  console.log('Header slot provided')
}

// Get slot names
const slotNames = Object.keys(slots)

// Call a slot programmatically
const defaultSlot = slots.default?.()
</script>

<template>
  <div>
    <slot name="header" />
    <slot />
  </div>
</template>

Usage Notes

  • Only available in setup() or <script setup>
  • Returns a reactive object
  • Slots are functions that return VNodes when called
  • Useful for advanced slot manipulation

Checking Slot Presence

import { useSlots } from 'vue'

const slots = useSlots()

// Check if default slot has content
const hasDefaultSlot = !!slots.default

// Check named slots
const hasHeader = !!slots.header
Must be called inside setup() or <script setup>. Cannot be called in async functions after an await statement.

useAttrs()

Returns the attrs object from the setup context. Type Signature:
function useAttrs(): SetupContext['attrs']

Returns

The fallthrough attributes object with the same structure as $attrs in the Options API.

Example

<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()

// Access attributes
console.log(attrs.id)
console.log(attrs.class)

// Check for specific attributes
if (attrs.disabled !== undefined) {
  console.log('Component is disabled')
}
</script>

Fallthrough Attributes

<!-- Parent.vue -->
<MyComponent id="foo" class="bar" data-test="baz" />

<!-- MyComponent.vue -->
<script setup>
import { useAttrs } from 'vue'

const attrs = useAttrs()

// attrs = {
//   id: 'foo',
//   class: 'bar',
//   'data-test': 'baz'
// }
</script>

<template>
  <!-- Manually apply attrs -->
  <div v-bind="attrs">Content</div>
</template>

<script>
// Disable automatic attribute inheritance
export default {
  inheritAttrs: false
}
</script>

Usage Notes

  • Only available in setup() or <script setup>
  • Returns a reactive object
  • Includes all attributes not declared as props or emits
  • Useful when inheritAttrs: false is set

Common Use Cases

import { useAttrs } from 'vue'

const attrs = useAttrs()

// Separate class and other attrs
const { class: className, ...otherAttrs } = attrs

// Apply attrs conditionally
const wrapperAttrs = computed(() => ({
  ...attrs,
  class: ['wrapper', attrs.class]
}))
Must be called inside setup() or <script setup>. Cannot be called in async functions after an await statement.

useModel()

Helper for creating two-way binding with parent components. Used at runtime when not using compiler macros. Type Signature:
function useModel<
  T extends Record<string, any>,
  K extends keyof T
>(
  props: T,
  name: K,
  options?: DefineModelOptions<T[K]>
): ModelRef<T[K]>

type DefineModelOptions<T = any> = {
  get?: (v: T) => any
  set?: (v: any) => any
}
props
object
The component props object.
name
string
The name of the prop to bind. The component must also emit an update:propName event.
options
DefineModelOptions
Optional transformers:
  • get: Transform value when reading
  • set: Transform value when writing

Example

<script setup>
import { useModel } from 'vue'

const props = defineProps(['modelValue', 'count'])
const emit = defineEmits(['update:modelValue', 'update:count'])

// Basic usage
const model = useModel(props, 'modelValue')

// Named model
const count = useModel(props, 'count')

// Update the model
model.value = 'new value' // emits 'update:modelValue'
count.value++ // emits 'update:count'
</script>

With Transformers

<script setup>
import { useModel } from 'vue'

const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const model = useModel(props, 'modelValue', {
  // Transform to uppercase when reading
  get: (val) => val?.toUpperCase(),
  // Transform to lowercase when writing
  set: (val) => val?.toLowerCase()
})

// Reading: model.value returns uppercase
// Writing: model.value = 'FOO' emits lowercase 'foo'
</script>

Usage Notes

  • Creates a computed ref that syncs with the parent prop
  • Automatically emits the update event when the ref is modified
  • Handles both controlled (with v-model) and uncontrolled usage
  • The prop must be declared in defineProps()
  • The update event must be declared in defineEmits()
Prefer using defineModel() in <script setup> which provides a more convenient API.

useTemplateRef()

Creates a ref that will be populated with the corresponding template element or component instance. Type Signature:
function useTemplateRef<T = unknown>(
  key: string
): Readonly<ShallowRef<T | null>>

type TemplateRef<T = unknown> = Readonly<ShallowRef<T | null>>
key
string
The ref name matching the ref attribute in the template.

Returns

A readonly shallow ref that will be populated after the component is mounted.

Example

<script setup>
import { useTemplateRef, onMounted } from 'vue'

// Create a template ref
const inputRef = useTemplateRef('input')

onMounted(() => {
  // Access the DOM element after mount
  inputRef.value?.focus()
})
</script>

<template>
  <input ref="input" type="text" />
</template>

With Component Refs

<script setup>
import { useTemplateRef } from 'vue'
import ChildComponent from './ChildComponent.vue'

const childRef = useTemplateRef('child')

// Access child component instance
function callChildMethod() {
  childRef.value?.someMethod()
}
</script>

<template>
  <ChildComponent ref="child" />
</template>

With v-for

<script setup>
import { useTemplateRef, onMounted } from 'vue'

const items = ['a', 'b', 'c']
const itemRefs = useTemplateRef('items')

onMounted(() => {
  // itemRefs.value is an array of elements
  console.log(itemRefs.value) // [div, div, div]
})
</script>

<template>
  <div v-for="item in items" :key="item" ref="items">
    {{ item }}
  </div>
</template>

Usage Notes

  • Only available in setup() or <script setup>
  • Returns a readonly ref to prevent accidental overwrites
  • The ref is populated after the component is mounted
  • The value is null before mount and after unmount
  • Use with onMounted() or watchEffect() to safely access the value
The returned ref is readonly. The value is automatically set by Vue and should not be manually assigned.

useId()

Generates a unique ID for the current component instance. Useful for accessibility attributes. Type Signature:
function useId(): string

Returns

A unique string ID in the format v-${uid} where uid is unique per component instance.

Example

<script setup>
import { useId } from 'vue'

const id = useId()
const inputId = `${id}-input`
const labelId = `${id}-label`
</script>

<template>
  <div>
    <label :id="labelId" :for="inputId">Name</label>
    <input :id="inputId" :aria-labelledby="labelId" type="text" />
  </div>
</template>

Multiple IDs in One Component

<script setup>
import { useId } from 'vue'

const baseId = useId()
const formIds = {
  name: `${baseId}-name`,
  email: `${baseId}-email`,
  message: `${baseId}-message`
}
</script>

<template>
  <form>
    <input :id="formIds.name" type="text" />
    <input :id="formIds.email" type="email" />
    <textarea :id="formIds.message" />
  </form>
</template>

SSR-Safe IDs

<script setup>
import { useId } from 'vue'

// The ID is stable across server and client
const id = useId()

// Safe for SSR - no hydration mismatch
</script>

<template>
  <div :id="id">Content</div>
</template>

Usage Notes

  • Only available in setup() or <script setup>
  • Generates a stable ID that’s consistent across SSR and client hydration
  • Each call to useId() returns the same ID for a given component instance
  • IDs are unique across different component instances
  • The prefix can be customized via app.config.idPrefix

Customizing ID Prefix

import { createApp } from 'vue'

const app = createApp(App)

// Customize the ID prefix (default is 'v')
app.config.idPrefix = 'my-app'

// IDs will now be formatted as 'my-app-{uid}'

Common Use Cases

<script setup>
import { useId } from 'vue'

const id = useId()
</script>

<template>
  <!-- Form label/input association -->
  <label :for="`${id}-input`">Name</label>
  <input :id="`${id}-input`" />
  
  <!-- ARIA relationships -->
  <button :aria-describedby="`${id}-desc`">Help</button>
  <div :id="`${id}-desc`">Description text</div>
  
  <!-- Modal dialogs -->
  <button :aria-controls="`${id}-dialog`">Open</button>
  <dialog :id="`${id}-dialog`">Content</dialog>
</template>
useId() is particularly useful for building accessible components that need unique IDs for ARIA attributes.

getCurrentInstance()

Returns the current component instance. For advanced use cases only. Type Signature:
function getCurrentInstance(): ComponentInternalInstance | null

Example

import { getCurrentInstance } from 'vue'

const instance = getCurrentInstance()
if (instance) {
  console.log(instance.proxy) // Component public instance
  console.log(instance.props) // Props
  console.log(instance.emit) // Emit function
}
This is an advanced API intended for library authors. Most applications should not need this. Use the dedicated composition API functions instead.

When to Use

  • Building plugins or composables that need access to internal instance
  • Advanced integrations with third-party libraries
  • Debugging or development tools

When NOT to Use

  • Regular application code should use dedicated APIs:
    • Use useSlots() instead of instance.slots
    • Use useAttrs() instead of instance.attrs
    • Use inject() instead of accessing instance.provides

Best Practices

Prefer Dedicated APIs

// Good: Use dedicated helpers
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()

// Avoid: Using getCurrentInstance for common tasks
import { getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
const slots = instance.slots // Not recommended

Use defineModel() Over useModel()

<!-- Good: Use defineModel() macro -->
<script setup>
const model = defineModel()
</script>

<!-- Avoid: Manual useModel() setup -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const model = useModel(props, 'modelValue')
</script>

Call Helpers Synchronously

import { useSlots, useAttrs } from 'vue'

// Good: Called synchronously
const slots = useSlots()
const attrs = useAttrs()

// Bad: Called after await
await someAsyncOperation()
const slots = useSlots() // May not work correctly

Use useTemplateRef() for Type Safety

<script setup lang="ts">
import { useTemplateRef } from 'vue'

// Type-safe template ref
const inputRef = useTemplateRef<HTMLInputElement>('input')

onMounted(() => {
  // TypeScript knows this is an HTMLInputElement
  inputRef.value?.focus()
})
</script>

Build docs developers (and LLMs) love