Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.2 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||
|---|---|---|---|---|---|---|---|---|
| useAttrs() Object Is Not Reactive | MEDIUM | Watching attrs directly does not trigger - use onUpdated() or convert to props | gotcha |
|
useAttrs() Object Is Not Reactive
Impact: MEDIUM - The object returned by useAttrs() is NOT reactive. While it always reflects the latest fallthrough attributes, you cannot use watch() or watchEffect() to observe its changes. Watchers on attrs properties will NOT trigger when attributes change.
Task Checklist
- Never use
watch()to observe attrs changes - it won't trigger - Use
onUpdated()lifecycle hook for side effects based on attrs - Convert frequently-accessed attrs to props if you need reactivity
- Remember attrs ARE always current in templates and event handlers
Incorrect:
<script setup>
import { watch, useAttrs } from 'vue'
const attrs = useAttrs()
// WRONG: This watcher will NEVER trigger when attrs change!
watch(
() => attrs.someAttr,
(newValue) => {
console.log('Attribute changed:', newValue)
// This callback never runs on attr changes
}
)
// WRONG: watchEffect also doesn't track attrs
watchEffect(() => {
console.log(attrs.class) // Only runs on mount, NOT when class changes
})
</script>
Correct:
<script setup>
import { onUpdated, useAttrs } from 'vue'
const attrs = useAttrs()
// CORRECT: Use onUpdated to access latest attrs
onUpdated(() => {
console.log('Current attrs:', attrs)
// Perform side effects with latest attrs here
})
</script>
<script setup>
import { watch } from 'vue'
// CORRECT: If you need reactivity, declare it as a prop
const props = defineProps({
someAttr: String
})
// Now you can watch it
watch(
() => props.someAttr,
(newValue) => {
console.log('Attribute changed:', newValue)
}
)
</script>
Why Attrs Are Not Reactive
Vue's official documentation states:
"Note that although the attrs object here always reflects the latest fallthrough attributes, it isn't reactive (for performance reasons). You cannot use watchers to observe its changes."
This is a deliberate design decision for performance - making attrs reactive would add overhead to every component that uses fallthrough attributes.
When Attrs DO Reflect Current Values
Despite not being reactive, attrs always have current values in these contexts:
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
// In event handlers - attrs are current when handler executes
function handleClick() {
console.log(attrs.class) // Current value at click time
}
</script>
<template>
<!-- In templates - always current -->
<div :class="attrs.class">
{{ attrs.title }}
</div>
<!-- Using v-bind to spread all attrs -->
<input v-bind="attrs" />
</template>
Computed Properties with Attrs
Computed properties that reference attrs will update when the component re-renders:
<script setup>
import { computed, useAttrs } from 'vue'
const attrs = useAttrs()
// This DOES update - because computed re-evaluates on render
const hasCustomClass = computed(() => {
return !!attrs.class
})
</script>
<template>
<div v-bind="attrs">
<span v-if="hasCustomClass">Has custom styling</span>
</div>
</template>
Note: The computed updates because the component re-renders when props/attrs change, not because attrs is reactive.
Alternative: Use getCurrentInstance() (Advanced)
For advanced use cases, you can access attrs through the component instance:
<script setup>
import { watch, getCurrentInstance } from 'vue'
const instance = getCurrentInstance()
// This may work in some cases, but is NOT officially supported
// getCurrentInstance() is an internal API and may change
watch(
() => instance?.attrs.someAttr,
(newValue) => {
console.log('Attribute changed:', newValue)
}
)
</script>
Warning:
getCurrentInstance()is an internal API. PreferonUpdated()or converting to props.