VueJS in-template variables
06 Sep 2025

One reason why we would want to create temporary variables in template is performance. Consider the following vue template code:

<template>
  <div v-for="x in collection">
    <div>{{ performHeavyCompute(x).result.a }}</div>
    <div>{{ performHeavyCompute(x).result.b }}</div>
  </div>
</template>

You will realize that in each iteration of the loop, we will have to execute the performHeavyCompute() method twice. Wouldn’t it be better if we set the result of performHeavyCompute() method to a variable and then reference it?

The other reason why it would be nice to have variables in the template is to improve readability. Conside the following vue template code:

<template>
  <div v-if="some?.deeply?.nested?.object">
    <div>{{ some.deeply.nested.object.propertyOne }}</div>
    <div>{{ some.deeply.nested.object.propertyTwo }}</div>
  </div>
</template>

As you can probably guess, a variable to set the long some.deeply.nested.object path can be really welcoming in terms of readability and cleanliness of the code.

Lets take a look at some solutions to improve the above code.

Using the v-for directive

If you have used some kind of a for-loop before in Javascript or any other programming language, we know that the syntax provides us a temporary “alias” variable to refer to the individual item in the collection. Therefore we can use a similar syntax using the v-for directive. Apply this approach to the above example code, we get this:

<template>
  <div v-for="x in collection">
    <template v-for="result in [performHeavyCompute(x).result]">
      <div>{{ result.a }}</div>
      <div>{{ result.b }}</div>
    </template>
  </div>
</template>

Kinda cool hack don’t you think? Using a built-in feature to achieve this. We can also use destructuring as well to further reduce the verbosity.

<template>
  <div v-for="x in collection">
    <template v-for="{ a, b } in [performHeavyCompute(x).result]">
      <div>{{ a }}</div>
      <div>{{ b }}</div>
    </template>
  </div>
</template>

Well, for those with keen eyes, you will realize that we omitted the :key attribute when we use the v-for directive. So whether it really improves the code readability and cleanliness or not, it is really up to you to decide.

Using a renderless component

Another solution is to use a purpose built component to create the temporary variables. Using the following code, thanks to fardolieri from the github rfc post regarding this topic, we make use of Vue component’s Scoped Slots feature.

const LocalScope = defineComponent(
  (props, ctx) => () => ctx.slots.default?.(ctx.attrs),
  { inheritAttrs: false }
) as unknown as new <T>(props: T) => {
  $props: T;
  $slots: { default?: (context: T) => unknown };
};

export default LocalScope;

I renamed from TempVar in the github post to LocalScope in the above code as I feel it has more a ring to the name. But you can definitely name whatever you want of course.

So making use of LocalScope component to our above examples, we get the following:

<template>
  <div v-for="x in collection">
    <LocalScope :result="performHeavyCompute(x).result" v-slot="{ result }">
      <div>{{ result.a }}</div>
      <div>{{ result.b }}</div>
    </LocalScope>
  </div>
</template>

What is also nice about the LocalScope component is that TypeScript inferencing still works on the Scoped Slot variables.

Conclusion

So in summary, both approaches have their Pros and Cons, so it is up to you to decide which one suits the most in your use case. For me the LocalScope component approach makes more sense, as it has a cleaner and more intuitive syntax. Moreover, when we register the component as a global component, we can easily use it any where in our project. Actually if I were to have a bit of liberty to dream, wouldn’t it be nice if we can have a built-in directive like such:

<template>
  <div v-for="x in collection">
    <template v-alias:result="performHeavyCompute(x).result">
      <div>{{ result.a }}</div>
      <div>{{ result.b }}</div>
    </template>
  </div>
</template>

Happy dreaming! 💭