Jaime Jones

Frontend focused full stack enthusiast.

The Vue.js Ref Attribute

27 May 2020

In general, components should be structured so that needs bubble up. A child can emit out to a parent and the parent can respond. The child doesn’t need to respond to the parent in most cases. But what about in the cases where the child does need to respond to an action in the parent? This is where we can take advantage of the ref attribute in Vue.js. It is useful for both manipulating DOM elements within a parent component and solving this issue of a parent component needing to manipulate a child component in some way.

To use the ref attribute, you simply throw it onto an existing tag and then it can be accessed from within the Vue instance off of this.$refs. I’ll go over examples of this with just a basic HTML element, a single child component, and a child component within a v-for. I’ll finish up with some caveats about the usage of refs.

Using on an HTML element

A ref attribute can be placed on a regular HTML element.

<div ref="myDiv" class="text--navy">Hello!</div>

<input ref="myInput" type="text">

We can now access both our div and our input via this.$refs within our Vue instance. When a ref is applied to an HTML element like this, we get back the element when we access it on this.$refs, just as if we had selected it via something like document.querySelector. This means that we have access to the same methods such as modifying classes or focusing inputs.

console.log(this.$refs.myDiv);
// <div ref="myDiv" class="text--navy">Hello!</div>
this.$refs.myDiv.textContent = 'Hey there!';

this.$refs.myInput.focus();

Using on a single component

A ref attribute can also be placed on a child component tag.

<puppy-cmp ref="myPuppy"></puppy-cmp>

By placing a ref on a component, we can now access it via this.$refs. However, unlike when a ref was placed on a basic HTML tag, accessing the ref of our child component will actually give us back the Vue instance for that child component! This is extremely powerful as we will then have access to all of the data and methods on that instance.

console.log(this.$refs.myPuppy);
// returns the instance of puppy-cmp

this.$refs.myPuppy.name = 'Clifford';

this.$refs.myPuppy.petPuppy();

Using on a component within a v-for

A ref can even be placed on a child component tag when it is within a v-for.

<template v-for="kitten in kittens">
  <kitten-cmp
    ref="myKittens"
    :kitten="kitten"
    :key="kitten.id">
  </kitten-cmp>
</template>

By placing a ref on a component while it is within a v-for, it also becomes accessible via this.$refs. But instead of getting back a single instance when accessing it, we instead get back an array of component instances. Drilling down into that array, we can still access all of the data and methods on each instance!

console.log(this.$refs.myKittens);
// returns an array of component instances of kitten-cmp

// we can find a specific kitten within the array if we want
let specificKitten = this.$refs.myKittens.find(x => x.id == passedId);

if (specificKitten) {
  specificKitten.name = 'Mittens';
  specificKitten.$el.scrollIntoView();
}

The caveats

Using refs to interact with both the DOM and child components is both useful and powerful, but there are some important caveats to it. refs are not reactive. This means that if you attempt to watch a value on the child component for changes, the parent will not react to those changes because it is not observing the data. Because of this, refs should not be used within computed, watch, or your template to influence changes in the parent. If the child needs to communicate to the parent, it should be done through emitting events or keeping track of props.

In spite of these caveats though, refs still give us a great way to handle certain edge cases and be able to manipulate a child component from the parent.