Not Inside the LoopâBut Before It: Understanding Computed in Vueâs Rendering Flow
Using computed inside loops breaks its purpose as a cached, reusable derived value, since loops should only consume data while computed should prepare it once per change, not recreate it per iteration.
Thereâs a moment where using computed inside a loop feels like a good idea.
You have a list.
You want to transform each item.
And it seems natural to write something like:
<div v-for="item in items" :key="item">
{{ computed(() => item * 2).value }}
</div>It works.
But something about it is off.
Not visibly wrongâbut structurally misplaced.
And to understand why, you have to see where computed actually belongs.
A Loop Doesnât ComputeâIt Iterates
When Vue renders a loop:
<div v-for="item in items" :key="item">
{{ item }}
</div>It doesnât âre-run logicâ in the way we often imagine.
It simply:
Takes a value
The loop is not where computation happens.
Itâs where results are used.
Where Computation Actually Lives
Now consider a computed property:
const doubled = computed(() => {
return items.value.map(n => n * 2)
})And the template:
<div v-for="item in doubled" :key="item">
{{ item }}
</div>Here, something subtle changes.
The transformation happens once.
The loop simply consumes it.
The computation is no longer tied to renderingâit exists before it.
The Difference Is Not About Syntax
Both approaches can produce the same output.
But they describe two very different flows.
In one:
- each iteration performs its own calculation
- logic lives inside the rendering process
In the other:
- data is prepared ahead of time
- rendering becomes a simple projection of state
One mixes responsibilities.
The other separates them.
Why Computed Inside Loops Feels Wrong
A computed property is designed to be:
- stable
- cached
- reused
It represents a piece of derived state.
But when you create it inside a loop, it becomes:
- short-lived
- recreated on every render
- disconnected from meaningful dependencies
It loses everything that makes it useful.
Instead of being a shared value, it becomes a disposable function.
A Subtle Performance Shift
Consider this:
<div v-for="item in items" :key="item">
{{ item * 2 }}
</div>This runs the multiplication every render.
For small operations, thatâs fine.
But scale it up:
- heavy transformations
- large lists
- multiple re-renders
And the cost starts repeating.
Now compare with:
const processed = computed(() => {
return items.value.map(item => heavyCalculation(item))
})Now:
- the expensive work runs once per change
- the result is cached
- the loop simply displays it
The difference is not just performance.
Itâs where the work happens.
Two Levels of Thinking
At this point, the distinction becomes clearer.
A loop operates at the UI level:
- it renders
- it repeats
- it displays
A computed operates at the data level:
- it derives
- it transforms
- it prepares
When these two are aligned, the system feels natural.
When they are mixed, things start to feel offâeven if they still work.
A More Useful Question
Instead of asking:
âCan I use computed here?â
Ask:
âIs this a reusable piece of state, or just a one-off calculation?â
If itâs reusableâ
it belongs in computed.
If itâs trivial and localâ
it can stay in the loop.
A Final Thought
Itâs easy to think of loops as places where logic lives.
But in Vue, they are closer to mirrors.
They reflect what already exists.
And computed properties are not meant to live inside reflections.
They are meant to shape what is being reflected in the first place.