In Vue 3.5, a new built-in composable was added: useTemplateRef.
It’s a much better way to access DOM elements directly than what we had before.
Here’s the most basic example:
When this component mounts, we’ll immediately put focus into the search input. To do this, we first set up our template ref using useTemplateRef and passing it the key “search”. Then, in our template, we add a ref attribute with the same key to our input element.
Behind the scenes, Vue connects these two pieces, and now we can directly access our input element in our Vue code!
Before we move on to more advanced ways to use this and some best practices, let’s first talk about why this is important and useful.
Template refs aren’t new to Vue. Before, we would access them by declaring a regular ref like this:
There are two main differences here:
ref(null) instead of useTemplateRef('search')ref attribute to the variable itself instead of using a stringThe code itself isn’t all that different, but this implementation presents us with three different challenges:
First, we can’t tell the difference between a “regular” ref, and a template ref. We have to look at the template to see how it’s being used. We can’t just look at the script block to get that info.
With useTemplateRef, we know at a glance, because it’s defined using useTemplateRef instead of ref like everything else is.
Second, writing composables that manipulate the DOM requires passing these template refs back and forth all of the time. If we took this focus example and bundled it up into a composable, we’d get this:
To use it, we first have to create the ref and then pass it in:
Instead, with useTemplateRef, we can refactor it to be simpler and more flexible as well:
We’ll also have to update our template to use the “focus” key:
With this update, we no longer have to pass the ref around! There are some other cool things we can do when writing composables with useTemplateRef, but we’ll tackle that later on in this article.
Lastly, the useTemplateRef composable gives us way better type inference in two ways:
If we use useTemplateRef in a component or a composable (and the element we use it on is static), we get automatic type inference of that element. This means that if it’s a textarea we get the HTMLTextAreaElement type, and if it’s a div we’ll get back HTMLDivElement.
We also get fantastic autocomplete for the keys we pass into useTemplateRef, because TypeScript knows all about how it should be connected. This makes for a really great developer experience!
Okay, now that I’ve convinced you why we need it, let’s take a step deeper and look at how we can use this in our apps.
We’ve already seen the most basic way to use it:
There are a few things we need to do:
useTemplateRefonMounted to make sure the component has been rendered before we try to do anything with itnull, especially if you’re using v-if on the element!We can also use the created template ref like we did with previous template refs, and use it directly on the template:
If you use useTemplateRef inside of a v-for, you’ll actually get back an array of elements to work with. But be careful here, there’s no guarantee that they’ll be in the same order, so you’ll have to do a bit of extra work to keep things sorted:
(I’ve simplified the above example to keep it clear, but make sure your forms are actually accessible!)
Sometimes you’ll be making changes that cause a layout shift, or something else where you need to wait for the page to fully update before doing a second thing with it. In this case, nextTick is your friend:
I’ll provide a more complex example of a composable that uses all of these techniques at the end of the article, but for now let’s explore some other things we can do with useTemplateRefs.
Let’s go one step further and apply the Flexible Arguments Pattern so we can choose whether we’ll pass in our own template ref or not:
Normally, we’d use ref to make sure we’re always getting a ref in our composable:
But here, we can’t pass a ref into useTemplateRef so we have to use a good old fashioned OR to check and create our own template ref if needed.
We can make this even better though. We can modify our input so we can pass either a string or a ref, so we can control what the key is:
This gives us ultimate control over how we use this composable. We can pass in our own template ref:
Or we can pass in our own string:
And if we don’t really care, we can use the default ref. But we have to know what string the composable is using as the key:
To solve that, we can update our composable to return the default key. While we’re at it, we can also make sure that it returns whatever key is currently being used, whether we’ve passed in our own or are using the default:
Now we don’t have to guess at what the composable will be using as the key:
Here’s an example composable from Mastering Nuxt that we use to add some scrolling functionality to our AI chat app. It shows and hides a button that is used to scroll to the bottom, and if we’re already at the bottom, keeps us there as new messages come in:
I think that useTemplateRef is one of the most interesting features added to Vue recently, and you should really give it a try.
It makes dealing with DOM elements so much nicer, giving us more flexibility and a better developer experience.
You can check out the docs here to learn more, and also learn about the advanced typing you can do with it.
If you enjoyed this article and want to know when I publish more (along with some great tips on using Vue), check out my weekly newsletter on all things Vue and Nuxt.