VOOZH about

URL: https://blog.logrocket.com/understanding-vue-refs/

⇱ Understanding refs in Vue - LogRocket Blog


2024-05-30
2751
#vue
Nwose Lotanna
3812
👁 Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

Editor’s note: This article was last updated by Ikeh Akinyemi on 30 May 2024 to discuss using refs with Vue’s reactivity system and using Vue watchers to watch refs and reactive state.

👁 Understanding Refs In Vue

In this tutorial, we’ll demonstrate how to reference HTML elements in your Vue.js components. Vue.js is a progressive JavaScript framework that focuses on the view layer, and refs are an important feature in Vue that allows you to easily interact with and manipulate DOM elements. This tutorial is suited for all levels of Vue experience, including beginners.

Here are a few prerequisites before we get started:

  • Node.js version 10.x or above. Verify your version by running node -v in your terminal/command prompt
  • npm 6.7 or above
  • A code editor; I highly recommend VS Code
  • Vue.js 3 installed globally on your machine
  • Vue CLI 3.0 installed on your machine. To do this, uninstall the old CLI version first with npm uninstall -g vue-cli, then install the new one using npm install -g @vue/cli
  • Download the Vue starter project
  • Unzip the downloaded project, navigate into it, and run npm install to keep all the dependencies up to date

🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

Understanding refs in Vue.js

Refs are Vue.js instance properties used to register or indicate a reference to HTML elements or child elements in the template of your application.

If a ref attribute is added to an HTML element in your Vue template, you’ll be able to reference that element or even a child element in your Vue instance. You can also access the DOM element directly; it is a read-only attribute and returns an object.

Vue’s ref helps you “reference” DOM elements more efficiently than JavaScript’s getElementById, which is known to create performance issues.

The ref attribute makes a DOM element selectable by serving as the key in the parent $ref attribute. Putting a ref attribute in an input element, for example, will expose the parent DOM node as this.$refs.input, or you can format it as this.refs["input"].

For a visual guide on referencing with $refs in Vue.js, check out this video tutorial.

this.refs in Vue

You can manipulate a DOM element by defining methods on the element’s reference. For example, you could focus on an input element using this:

this.$refs["input"].focus()

In this way, refs can be used just like the document.querySelector('.element') in JavaScript or the $('.element') in jQuery. While document.querySelector() performs some of the same functions as refs, refs are more efficient because they give you direct access to the specific element you need. On the other hand, document.querySelector() simply returns the first element in your DOM that matches the selector you specified.

$refs can be accessed both inside the Vue.js instance and outside of it. However, they are not data properties, so they are not reactive. On template inspection in your browser, $refs does not show up at all because it is not an HTML attribute; it is only a Vue template attribute.

Getting started with Vue.js refs

If you followed this post from the start, you should have downloaded the Vue starter project and opened it up on VS Code. Open the components folder and copy this into the test.vue file:

<template>
 <div>
 <h2>Hello this is for refs man!</h2>
 <p>You have counted {{this.counter}} times</p>
 <input type="text" ref="input">
 <button @click="submit">Add 1 to counter</button>
 </div>
</template>
<script>
export default {
 name: 'Test',
 data(){
 return {
 counter: 0
 }
 },
 methods: {
 submit(){
 this.counter++;
 console.log(this.ref)
 }
 }
}
</script>

After copying the provided code into your test.vue file, run the following command in your terminal to start the development server:

npm run serve

Once the development server is running, open your browser and navigate to the provided local URL (e.g., http://localhost:8080). You should see a simpler counter application with an input field and a button. You will see that the counter gets updated on click, but when you open your developer tools in the browser, you will notice that it logs undefined:

👁 Demo Counter Application

It is important to get the syntax right when accessing refs in Vue. In the code example above, when we log this.ref inside the submit method, it returns undefined. This means that Vue doesn’t raise an error for this.ref, even though it is incorrect syntax.

Copy the code below into the test.vue file:

<template>
 <div>
 <h2>Hello this is for refs man!</h2>
 <p>You have counted {{this.counter}} times</p>
 <input type="text" ref="input">
 <button @click="submit">Add 1 to counter</button>
 </div>
</template>
<script>
export default {
 name: 'Test',
 data(){
 return {
 counter: 0
 }
 },
 methods: {
 submit(){
 this.counter++;
 console.log(this.$refs)
 }
 }
}
</script>

When you run restart the development server and inspect the result of the new code implementation, you will notice that it now returns an object:

👁 Inspecting Test.Vue

A quick look at the code block will reveal the correct syntax: inside the template, it is called ref, but when we referred to it inside the script, it was called $refs. This is important to note so we don’t get an undefined return.

The ref syntax that is used inside the template is an attribute given to an element. $refs, on the other hand, is a collection of the elements in your application that have been given the ref attribute. You can access every single possible property of the referenced element, including the element, as it is in the template.

Using ref in the Composition API

In Vue 3’s Composition API, you can create refs using the ref function. By using refs in the Composition API, you can easily access and manipulate DOM elements, store reactive values, and integrate with other Composition API features like watch and computed.

Using refs with Vue’s reactivity system

In Vue 3, the reactivity system is built on top of the reactive and ref functions. Both reactive and ref allow you to create reactive state in your components, but they have some differences in their usage and behavior.

Here’s an example that demonstrates the usage of ref and reactive in a component:

<template>
 <div>
 <p>Count: {{ count }}</p>
 <p>Message: {{ state.message }}</p>
 <button @click="increment">Increment</button>
 </div>
</template>

<script>
import { ref, reactive } from 'vue';

export default {
 setup() {
 const count = ref(0);
 const state = reactive({
 message: 'Hello',
 });

 function increment() {
 count.value++;
 }

 return {
 count,
 state,
 increment,
 };
 },
};
</script>

The ref function takes an initial value and returns a reactive reference to that value. And the refs can hold any value type, including primitives, objects, and arrays:

<template>
 <div>
 <input type="text" ref="input" v-model="inputValue" />
 <button @click="focusInput">Focus Input</button>
 <p>Input value: {{ inputValue }}</p>
 </div>
</template>

<script>
import { ref, onMounted } from 'vue';

export default {
 setup() {
 const input = ref(null);
 const inputValue = ref('');

 onMounted(() => {
 input.value.focus();
 });

 function focusInput() {
 input.value.focus();
 }

 return {
 input,
 inputValue,
 focusInput,
 };
 },
};
</script>

In the above example, we used ref="input" in the template to create a reference to the <input> element. Then, in the setup function, we used const input = ref(null) to create a ref that would store the reference to the <input> element.

Next, we have const inputValue = ref(''), which creates another ref that is used to store the value of the <input> element and is bound to the v-model directive in the template. With these two references, you can automatically call input.value.focus() inside the onMounted lifecycle Hook, focusing the <input> element when the component is mounted. Then the focusInput function is defined to programmatically focus the <input> element when the button is clicked.

Getting DOM elements in Vue.js

Let’s try logging some of the properties that might be of interest to us. Your test.vue file should look like this:

<template>
 <div>
 <h2>Hello this is for refs man!</h2>
 <p>You have counted {{this.counter}} times</p>
 <input type="text" ref="input">
 <button @click="submit">Add 1 to counter</button>
 </div>
</template>
<script>
export default {
 name: 'Test',
 data(){
 return {
 counter: 0
 }
 },
 methods: {
 submit(){
 this.counter++;
 console.log(this.$refs)
 }
 }
}
</script>
<style scoped>
p , input, button{
 font-size: 30px;
}
input, button{
 font-size: 20px;
}
ul {
 list-style-type: none;
 padding: 0;
}
li {
 display: inline-block;
 margin: 0 10px;
}
a {
 color: #42b983;
}
</style>

The application on your browser should look like this:

👁 Test Application Running In Browser

Displaying HTML elements in Vue.js

To display the HTML element as it is in the DOM, go into the submit method and change the methods code to the following:

methods: {
 submit(){
 this.counter++;
 console.log(this.$refs.input)
 }
 }

The input here is the reference name you created earlier inside the element (ref="input"). It can be any name of your choice.


Over 200k developers use LogRocket to create better digital experiences

👁 Image
Learn more →

Displaying the HTML input value

To display the HTML element input value — the string that was typed into the text box in the user interface — go into the submit method and change the code like so:

methods: {
 submit(){
 this.counter++;
 console.log(this.$refs.input.value)
 }
 }

This displays exactly the string you type in, demonstrating functionality similar to what can be achieved using querySelector in vanilla JavaScript or jQuery’s selector methods.

Displaying a Vue element’s URL

The webpage in which the element can be found is also one of the many things that can be displayed with the Vue ref. Go into the submit method and change the code to this:

methods: {
 submit(){
 this.counter++;
 console.log(this.$refs.input.baseURI)
 }
}

There are many other things you can both access and log with the ref just by the information on the returned object.

Handling conditionals in Vue.js

Vue.js refs can also be used inside elements that generate multiple items in the DOM, like conditional statements where v-for directives are used. Instead of returning a single object, refs return an array of the items when called. To illustrate this, let’s create a simple list:

<template>
 <div>
 <p v-for="car in 4" v-bind:key="car" ref="car"> I am car number {{car}}</p>
 <button @click="submit">View refs</button>
 </div>
</template>
<script>
export default {
 name: 'Test',
 data(){
 return {
 }
 },
 methods: {
 submit(){
 console.log(this.$refs)
 }
 }
}
</script>

When you run it in the development server again, it will look like this:

👁 App Preview With Conditionals

Integrating refs with Vue watchers

Vue watchers allow you to observe changes in reactive data and perform actions based on those changes. In Vue 3, you can use the watch function to watch refs and reactive state.

To watch a ref, you can pass the ref directly as the first argument to the watch function. Here’s an example:

<template>
 <div>
 <input type="text" v-model="message" />
 <p>Reversed Message: {{ reversedMessage }}</p>
 </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
 setup() {
 const message = ref('');
 const reversedMessage = ref('');

 watch(message, (newValue) => {
 reversedMessage.value = newValue.split('').reverse().join('');
 });

 return {
 message,
 reversedMessage,
 };
 },
};
</script>

In this example, we have a message ref that is bound to an input field using v-model. We also have a reversedMessage ref that will display the reversed version of the message.

We use the watch function to watch the message ref. Whenever the value of message changes, the callback function is triggered, and it reverses the message and assigns it to reversedMessage. You can also watch multiple refs or a combination of refs and reactive state by passing an array to the watch function:

watch([ref1, ref2, reactiveState], ([newRef1, newRef2, newReactiveState]) => {
 // Callback function triggered when any of the watched values change
});

Use cases for Vue watch with refs

  • Computed properties: Watchers can be used as an alternative to computed properties. If you have a complex computation that depends on multiple refs or reactive state, you can use a watcher to perform the computation whenever any of the dependencies change
  • Side effects: Watchers are useful for triggering side effects based on changes in refs or reactive state. For example, you can use a watcher to make an API call whenever a specific ref value changes
  • Validation: Watchers can be used to validate user input in real time. You can watch a ref that represents user input and perform validation checks whenever the input value changes
  • Data synchronization: Watchers can be used to synchronize data between different parts of your application. For example, if you have a ref that represents a selected item, you can use a watcher to update other parts of the UI based on the selected item
  • Animations: Watchers can be used to trigger animations based on changes in refs or reactive state. You can watch a ref and apply animations whenever its value changes

Here’s an example that demonstrates using a watcher for data synchronization:

<template>
 <div>
 <select v-model="selectedOption">
 <option value="A">Option A</option>
 <option value="B">Option B</option>
 <option value="C">Option C</option>
 </select>
 <p>Selected: {{ selectedOption }}</p>
 <p>Message: {{ message }}</p>
 </div>
</template>

<script>
import { ref, watch } from 'vue';

export default {
 setup() {
 const selectedOption = ref('A');
 const message = ref('');

 watch(selectedOption, (newValue) => {
 switch (newValue) {
 case 'A':
 message.value = 'You selected Option A';
 break;
 case 'B':
 message.value = 'You selected Option B';
 break;
 case 'C':
 message.value = 'You selected Option C';
 break;
 default:
 message.value = '';
 }
 });

 return {
 selectedOption,
 message,
 };
 },
};
</script>

In this example, we have a selectedOption ref bound to a select dropdown using v-model. We also have a message ref that will display a message based on the selected option. We use a watcher to watch the selectedOption ref. Whenever the selected option changes, the watcher callback function is triggered and updates the message ref based on the selected value.

By integrating refs with watchers, you can perform reactive updates and trigger actions based on changes in your component’s state. Watchers provide a powerful way to observe and respond to data changes in Vue 3.

Changes and best practices for Refs in Vue 3

Let’s see some changes and improvements introduced in Vue 3 that changed the ways we use refs:

  • The Composition API was introduced in Vue 3 with setup(): setup() is considered the best way to use refs. To use the Composition API, setup() acts as your point of entry whereby you can define your component’s reactive state and methods
  • Template refs: If you are familiar with Vue 2, it is easy to understand what template refs in Vue 3 should look like. In the Composition API, you need to declare the ref using the ref() function and expose it to the template
  • References with reactivity: Refs in Vue 3 are reactive by default. When you change the value of a ref by using the .value property to the ref object, it results in a reactive update inside the component, causing the automatic re-render of respective parts of the template
  • Ref typing: Vue 3 provides improved TypeScript support, including better type inference for refs. You can specify the type of a ref using generics, const count = ref<number>(0);

Conclusion

This article explored referencing HTML elements in your DOM in Vue.js. You can now access and log these elements by all the element properties, including value, child node, data attributes, and even the base URL that houses it.

It is important to note that refs get populated after the Vue instance has initialized and the component has been rendered, so using refs in computed properties is discouraged because it can directly manipulate child nodes.

Happy hacking!

LogRocket understands everything users do in your Vue apps.

Debugging Vue.js applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Vue mutations and actions for all of your users in production, try LogRocket.

👁 LogRocket Dashboard Free Trial Banner

LogRocket lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks.

With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.

Modernize how you debug your Vue apps — start monitoring for free.

👁 Image
👁 Image
👁 Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

👁 Image
Emmanuel John
Jun 17, 2026 ⋅ 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

👁 Image
Chizaram Ken
Jun 16, 2026 ⋅ 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension — no new framework required.

👁 Image
Ikeh Akinyemi
Jun 12, 2026 ⋅ 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo — with email/password login, Google OAuth, session persistence, and protected routes.

👁 Image
Chinwike Maduabuchi
Jun 9, 2026 ⋅ 13 min read
View all posts

Would you be interested in joining LogRocket's developer community?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now