Appearance
date: 2026-06-16 tags: [vue, vitepress, reactivity, footgun] status: active graduated_to:
A ref passed from a <script setup> template arrives UNWRAPPED — .value in the handler silently no-ops
Symptom — The pill filter in a VitePress Vue component (SkillFilter.vue) did nothing on click; only the text search worked, with no error thrown. The act/mode chips appeared active but never actually filtered the cards.
Root cause — In a <script setup> template, top-level refs are auto-unwrapped. So @click="toggle(act, k)" passes act.value (the plain reactive object), not the ref. The handler function toggle(map, key) { map.value[key] = !map.value[key] } then reads map.value — undefined on the unwrapped object — so the mutation hits undefined[key] and the real state never updates. In script scope act.value is still correct, which is why the read side (apply()) worked and masked the bug.
Fix — Mutate the object directly when it arrived from the template; the object inside the ref is still deeply reactive, so the in-place write triggers updates:
js
function toggle(map, key) {
map[key] = !map[key]
}(Or use reactive() instead of ref() for objects you hand around from the template.)
Guard — none automatic. Rule of thumb: a function called from a <script setup> template receives refs already unwrapped — never reach for .value on a template-passed argument.