Vue.js: Refs & DOM

Last updated: 2026-10-20

Template refs let you access DOM elements or child component instances directly—for example calling input.focus() or invoking a component method. Vue 3.5 introduced the more capable useTemplateRef composition API, with TypeScript type inference to match.

Template refs are an escape hatch—Vue recommends solving most problems with ref/reactive/props/emit. Reach for template refs only when you need to manipulate the DOM or a component instance directly.

1. What You'll Learn


2. The "Auto-Focus" Headache of a Login Form

(1) The Pain: How Do I Auto-Focus the Input After the Modal Opens?

Alice built a login modal that needs to focus the username input automatically:

JS
// ❌ Broken version: querySelector directly
onMounted(() => {
  const input = document.querySelector('.username-input')
  input.focus()  // ❌ Not the Vue way
})

The Vue philosophy: avoid manipulating the DOM directly. Use template refs instead.

Product manager Charlie:

"Alice, when the modal opens, the username field should be focused automatically so the user can start typing right away."

(2) The Vue Template Ref Fix

(3) The Payoff

After adopting template refs:


3. Basic ref Usage

(1) String refs (Vue 2 style, deprecated)

JS
// ❌ Not recommended: string ref
export default {
  mounted() {
    this.$refs.input.focus()
  }
}

(3) 5 Basic Operations

JS
// 1. Access the DOM element
inputRef.value  // the <input> element

// 2. Call DOM APIs
inputRef.value.focus()
inputRef.value.blur()
inputRef.value.select()
inputRef.value.scrollIntoView()

// 3. Read/modify DOM properties
inputRef.value.value  // the input's value
inputRef.value.disabled  // the disabled property
inputRef.value.style.color = 'red'  // change styles

// 4. Listen to DOM events (not recommended, use @event)
inputRef.value.addEventListener('focus', handler)

// 5. Access a child component instance (see 17.4)
childRef.value.someMethod()

▶ Example: Auto-focusing a login form

▶ Try it Yourself

Output:

TEXT
The page renders a form with Username and Password inputs plus a green Login button.
As soon as it loads, the caret sits in the Username field (its border shows focus), and a grey hint below reads Focus jumps to the Username field as soon as the page loads.
Submitting logs Login: <username> <password> to the console.
TEXT
[ Username            ]      ← focused as soon as the page loads
[ Password            ]
[ Login ]

Focus jumps to the Username field as soon as the page loads

# on submit (console)
Login: alice 123456

▶ Example: Auto-scrolling to the bottom (chat box)

▶ Try it Yourself

Output:

TEXT
A 150px-tall scrollable box shows three seeded messages, with an input and a Send button beneath it.
Typing a message and pressing Enter (or clicking Send) appends it to the list, clears the input, and scrolls the box to the newest message automatically.
TEXT
┌────────────────────────────────────┐
│ 👋 Welcome to the chat room        │
│ This is history message 2          │
│ This is history message 3          │
│ Hello                              │  ← just sent, scrolled into view
└────────────────────────────────────┘
[ Type a message...        ] [ Send ]

(1) Why Do We Need useTemplateRef?

With variable refs in <script setup> you need 2 names (the template ref and the variable), and it's easy to let them drift apart. useTemplateRef uses a single name and gives you stronger TypeScript inference.

(2) 5 Advantages

Advantage Description
Type inference TypeScript knows it's an HTMLInputElement
Rename safety Renaming updates everywhere in the IDE
No name mismatch A template ref that doesn't match the variable errors out
Simpler setup No const inputRef = ref(null) needed
Better DevTools Supported by Vue DevTools 5.x

(3) Side-by-Side Comparison

VUE
<!-- Old style: variable ref -->
<template>
  <input ref="usernameInput">
</template>

<script setup>
const { ref, onMounted } = Vue
const inputRef = ref(null)  // the name may not match the template
onMounted(() => inputRef.value.focus())
</script>

<!-- New style: useTemplateRef (recommended in Vue 3.5+) -->
<template>
  <input ref="usernameInput">
</template>

<script setup>
const { useTemplateRef, onMounted } = Vue
const inputRef = useTemplateRef('usernameInput')  // one name does it all
onMounted(() => inputRef.value.focus())
</script>

5. Accessing Child Component Instances

(1) Exposing Methods with defineExpose

(2) Access from the Parent

VUE
<template>
  <MyInput ref="myInputRef" v-model="searchQuery" />
  <button @click="focusInput">Focus Input</button>
</template>

<script setup>
const { ref } = Vue
import MyInput from './MyInput.vue'

const searchQuery = ref('')
const myInputRef = ref(null)

function focusInput() {
  myInputRef.value.focus()  // call the method exposed by the child
  myInputRef.value.select() // chaining also works
}
</script>

(3) 5 Common Use Cases

Scenario Child exposes Parent calls
Focus a field focus() inputRef.focus()
Clear a form clear() formRef.clear()
Reload data reload() tableRef.reload()
Open a modal open() modalRef.open()
Submit a form submit() formRef.submit()

6. Ref Arrays in v-for

(1) Basic Usage

(2) Dynamic refs (v-for with a dynamic count)

(3) 5 Things to Watch Out For

Point Description
Array order Matches the order of the v-for data
Reactivity Changes to the ref array need a watch
Conditional rendering Refs may not update after v-if
Dynamic count Use an object or function ref
Performance Many refs (100+) slow rendering down

7. Putting It Together: Parent-Child ref Validation

▶ Example: Parent calling a child's method (defineExpose)

▶ Expand runnable example (69 lines)
▶ Try it Yourself

Output:

TEXT
The child renders an Email field and a Password field, followed by a green Submit button.
Clicking Submit with an empty email shows a red ❌ Invalid email and moves focus to the email field.
With a valid-looking email but a password shorter than 6 characters you get ❌ Invalid password with focus in the password field;
a fully valid form shows the green ✅ Validation passed!.
TEXT
[ Email                ]
[ Password (min 6 chars) ]
[ Submit ]

empty email      → ❌ Invalid email       (focus moves to Email)
short password   → ❌ Invalid password    (focus moves to Password)
valid form       → ✅ Validation passed!

(1) 📊 5 Common Mistakes at a Glance

Mistake Symptom Fix
Misspelled template ref ref.value is null Make sure the names match
Accessing at the top level of setup ref.value is null Use onMounted
Accessing inside a modal Doesn't work Wait for the DOM with nextTick
Accessing a v-for ref array Indexes are off Keep the order stable with :key
Cross-component ref undefined Expose with defineExpose in the child

(2) 📊 5 Types of Refs, Compared

Type Example Best for
DOM element ref="inputRef" → HTMLInputElement Accessing input/div
Component instance ref="childRef" → component instance Calling child methods
v-for array ref="itemRefs" → Array Accessing list items
Function ref :ref="el => ..." → a single element Dynamic counts
String ref ref="name" → this.$refs Vue 2 style (not recommended)

❓ FAQ

Q When should I use template refs?
A 4 cases: (1) DOM operations (focus/scroll/canvas); (2) calling child methods; (3) integrating third-party libraries (ECharts/Mapbox); (4) measuring element size. For everything else use ref/reactive/props.
Q Does useTemplateRef require Vue 3.5+?
A Yes. It's only available from Vue 3.5. Use variable refs on 3.4 and below. TypeScript inference is better with useTemplateRef.
Q How do template refs differ from React's useRef?
A React's useRef returns a mutable ref; a Vue template ref returns a ref object. Vue 3.5+'s useTemplateRef is closer to the React useRef API.
Q Which methods should a child component expose?
A Only the ones the parent genuinely needs (focus / clear / validate). Keep internal methods private (encapsulation).
Q When does a v-for ref array update?
A On every v-for re-render. v-if switches update it too. You can watch the ref array to react to changes.
Q Do template refs conflict with provide/inject?
A No. Template refs are for "parent accessing child"; provide/inject is for "sharing data across levels". They compose fine.
Q How do I type a ref in TypeScript?
A Use useTemplateRef<HTMLInputElement>('usernameInput'). Or ref<HTMLInputElement | null>(null).

📖 Summary


📝 Exercises

  1. Basic (difficulty ⭐) Build a simple auto-focus form:

    • 1 input + 1 button
    • The input is focused automatically when the page loads
    • Clicking the button changes its label to "Submitted" and disables it
  2. Intermediate (difficulty ⭐⭐) Build a chat box that scrolls to the bottom automatically:

    • A message list (div container)
    • An input + send button
    • After sending, the message is appended and the list scrolls to the bottom
    • Render messages with v-for and manage their DOM with a ref array
  3. Challenge (difficulty ⭐⭐⭐) Build a complete "parent-child + template refs" system:

    1. FormValidator child: exposes validate() and reset()
    2. LoginPage parent: calls validate to check the form and shows errors on failure
    3. 5 input fields (username/email/password/phone/captcha)
    4. useTemplateRef (Vue 3.5+) + strong TypeScript types
    5. Focus the first input automatically when the modal opens
    6. On failure, focus the first invalid field
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏